diff options
99 files changed, 3487 insertions, 757 deletions
diff --git a/api/current.txt b/api/current.txt index 3318e06..5d35563 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2697,7 +2697,8 @@ package android.app { method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams); method public void closeContextMenu(); method public void closeOptionsMenu(); - method public void convertToOpaque(); + method public void convertFromTranslucent(); + method public void convertToTranslucent(android.app.Activity.TranslucentConversionListener); method public android.app.PendingIntent createPendingResult(int, android.content.Intent, int); method public final deprecated void dismissDialog(int); method public boolean dispatchGenericMotionEvent(android.view.MotionEvent); @@ -2883,6 +2884,10 @@ package android.app { field public static final int RESULT_OK = -1; // 0xffffffff } + public static abstract interface Activity.TranslucentConversionListener { + method public abstract void onTranslucentConversionComplete(boolean); + } + public deprecated class ActivityGroup extends android.app.Activity { ctor public ActivityGroup(); ctor public ActivityGroup(boolean); @@ -3134,6 +3139,32 @@ package android.app { ctor public AliasActivity(); } + public class AppOpsManager { + method public int checkOp(int, int, java.lang.String); + method public int checkOpNoThrow(int, int, java.lang.String); + method public void finishOp(int, int, java.lang.String); + method public void finishOp(int); + method public int noteOp(int, int, java.lang.String); + method public int noteOpNoThrow(int, int, java.lang.String); + method public static java.lang.String opToName(int); + method public int startOp(int, int, java.lang.String); + method public int startOpNoThrow(int, int, java.lang.String); + method public void startWatchingMode(int, java.lang.String, android.app.AppOpsManager.Callback); + method public void stopWatchingMode(android.app.AppOpsManager.Callback); + field public static final int MODE_ALLOWED = 0; // 0x0 + field public static final int MODE_ERRORED = 2; // 0x2 + field public static final int MODE_IGNORED = 1; // 0x1 + field public static final int OP_COARSE_LOCATION = 0; // 0x0 + field public static final int OP_FINE_LOCATION = 1; // 0x1 + field public static final int OP_GPS = 2; // 0x2 + field public static final int OP_MONITOR_LOCATION = 41; // 0x29 + field public static final int OP_NONE = -1; // 0xffffffff + } + + public static abstract interface AppOpsManager.Callback { + method public abstract void opChanged(int, java.lang.String); + } + public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 { ctor public Application(); method public void onConfigurationChanged(android.content.res.Configuration); @@ -10729,7 +10760,7 @@ package android.hardware.photography { } public static class CameraMetadata.Key { - ctor public CameraMetadata.Key(java.lang.String); + ctor public CameraMetadata.Key(java.lang.String, java.lang.Class<T>); method public final boolean equals(java.lang.Object); method public final java.lang.String getName(); method public final int hashCode(); @@ -10781,6 +10812,12 @@ package android.hardware.photography { method public int getScore(); } + public final class Rational { + ctor public Rational(int, int); + method public int getDenominator(); + method public int getNumerator(); + } + public final class Size { method public final int getHeight(); method public final int getWidth(); @@ -26510,6 +26547,9 @@ package android.view { method public void buildDrawingCache(boolean); method public void buildLayer(); method public boolean callOnClick(); + method public boolean canResolveLayoutDirection(); + method public boolean canResolveTextAlignment(); + method public boolean canResolveTextDirection(); method public boolean canScrollHorizontally(int); method public boolean canScrollVertically(int); method public void cancelLongPress(); @@ -26714,6 +26754,7 @@ package android.view { method public boolean isInEditMode(); method public boolean isInLayout(); method public boolean isInTouchMode(); + method public boolean isLayoutDirectionResolved(); method public boolean isLayoutRequested(); method public boolean isLongClickable(); method public boolean isOpaque(); @@ -26727,6 +26768,8 @@ package android.view { method public boolean isSelected(); method public boolean isShown(); method public boolean isSoundEffectsEnabled(); + method public boolean isTextAlignmentResolved(); + method public boolean isTextDirectionResolved(); method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); @@ -27230,7 +27273,9 @@ package android.view { method public void bringChildToFront(android.view.View); method protected boolean canAnimate(); method protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams); + method public void childAccessibilityStateChanged(android.view.View); method public void childDrawableStateChanged(android.view.View); + method public void childHasTransientStateChanged(android.view.View, boolean); method protected void cleanupLayoutState(android.view.View); method public void clearChildFocus(android.view.View); method public void clearDisappearingChildren(); @@ -27391,17 +27436,28 @@ package android.view { public abstract interface ViewParent { method public abstract void bringChildToFront(android.view.View); + method public abstract boolean canResolveLayoutDirection(); + method public abstract boolean canResolveTextAlignment(); + method public abstract boolean canResolveTextDirection(); + method public abstract void childAccessibilityStateChanged(android.view.View); method public abstract void childDrawableStateChanged(android.view.View); + method public abstract void childHasTransientStateChanged(android.view.View, boolean); method public abstract void clearChildFocus(android.view.View); method public abstract void createContextMenu(android.view.ContextMenu); method public abstract android.view.View focusSearch(android.view.View, int); method public abstract void focusableViewAvailable(android.view.View); method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point); + method public abstract int getLayoutDirection(); method public abstract android.view.ViewParent getParent(); method public abstract android.view.ViewParent getParentForAccessibility(); + method public abstract int getTextAlignment(); + method public abstract int getTextDirection(); method public abstract void invalidateChild(android.view.View, android.graphics.Rect); method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect); + method public abstract boolean isLayoutDirectionResolved(); method public abstract boolean isLayoutRequested(); + method public abstract boolean isTextAlignmentResolved(); + method public abstract boolean isTextDirectionResolved(); method public abstract void recomputeViewAttributes(android.view.View); method public abstract void requestChildFocus(android.view.View, android.view.View); method public abstract boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean); @@ -49952,6 +50008,7 @@ package org.json { method public org.json.JSONArray put(int, int) throws org.json.JSONException; method public org.json.JSONArray put(int, long) throws org.json.JSONException; method public org.json.JSONArray put(int, java.lang.Object) throws org.json.JSONException; + method public java.lang.Object remove(int); method public org.json.JSONObject toJSONObject(org.json.JSONArray) throws org.json.JSONException; method public java.lang.String toString(int) throws org.json.JSONException; } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d1efd0d1..fa746ba 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -745,6 +745,7 @@ public class Activity extends ContextThemeWrapper // protected by synchronized (this) int mResultCode = RESULT_CANCELED; Intent mResultData = null; + private TranslucentConversionListener mTranslucentCallback; private boolean mTitleReady = false; @@ -1382,6 +1383,7 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); getApplication().dispatchActivityStopped(this); + mTranslucentCallback = null; mCalled = true; } @@ -4886,22 +4888,61 @@ public class Activity extends ContextThemeWrapper /** * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a * fullscreen opaque Activity. - * + * <p> * Call this whenever the background of a translucent Activity has changed to become opaque. - * Doing so will allow the previously visible Activity behind this one to be stopped. Stopped - * apps consume no CPU cycles and are eligible for removal when reclaiming memory. + * Doing so will allow the {@link android.view.Surface} of the Activity behind to be released. + * <p> + * This call has no effect on non-translucent activities or on activities with the + * {@link android.R.attr#windowIsFloating} attribute. * + * @see #convertToTranslucent(TranslucentConversionListener) + * @see TranslucentConversionListener + */ + public void convertFromTranslucent() { + try { + mTranslucentCallback = null; + ActivityManagerNative.getDefault().convertFromTranslucent(mToken); + } catch (RemoteException e) { + // pass + } + } + + /** + * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} back from + * opaque to translucent following a call to {@link #convertFromTranslucent()}. + * <p> + * Calling this allows the Activity behind this one to be seen again. Once all such Activities + * have been redrawn {@link TranslucentConversionListener#onTranslucentConversionComplete} will + * be called indicating that it is safe to make this activity translucent again. Until + * {@link TranslucentConversionListener#onTranslucentConversionComplete} is called the image + * behind the frontmost Activity will be indeterminate. + * <p> * This call has no effect on non-translucent activities or on activities with the * {@link android.R.attr#windowIsFloating} attribute. + * + * @param callback the method to call when all visible Activities behind this one have been + * drawn and it is safe to make this Activity translucent again. + * + * @see #convertFromTranslucent() + * @see TranslucentConversionListener */ - public void convertToOpaque() { + public void convertToTranslucent(TranslucentConversionListener callback) { try { - ActivityManagerNative.getDefault().convertToOpaque(mToken); + mTranslucentCallback = callback; + ActivityManagerNative.getDefault().convertToTranslucent(mToken); } catch (RemoteException e) { // pass } } + /** @hide */ + void onTranslucentConversionComplete(boolean drawComplete) { + if (mTranslucentCallback != null) { + mTranslucentCallback.onTranslucentConversionComplete(drawComplete); + mTranslucentCallback = null; + } + } + /** * Adjust the current immersive mode setting. * @@ -4947,6 +4988,7 @@ public class Activity extends ContextThemeWrapper * @return The new action mode, or <code>null</code> if the activity does not want to * provide special handling for this action mode. (It will be handled by the system.) */ + @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { initActionBar(); if (mActionBar != null) { @@ -4961,6 +5003,7 @@ public class Activity extends ContextThemeWrapper * * @param mode The new action mode. */ + @Override public void onActionModeStarted(ActionMode mode) { } @@ -4970,6 +5013,7 @@ public class Activity extends ContextThemeWrapper * * @param mode The action mode that just finished. */ + @Override public void onActionModeFinished(ActionMode mode) { } @@ -5373,4 +5417,26 @@ public class Activity extends ContextThemeWrapper } } } + + /** + * Interface for informing a translucent {@link Activity} once all visible activities below it + * have completed drawing. This is necessary only after an {@link Activity} has been made + * opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn + * translucent again following a call to {@link + * Activity#convertToTranslucent(TranslucentConversionListener)}. + */ + public interface TranslucentConversionListener { + /** + * Callback made following {@link Activity#convertToTranslucent} once all visible Activities + * below the top one have been redrawn. Following this callback it is safe to make the top + * Activity translucent because the underlying Activity has been drawn. + * + * @param drawComplete True if the background Activity has drawn itself. False if a timeout + * occurred waiting for the Activity to complete drawing. + * + * @see Activity#convertFromTranslucent() + * @see Activity#convertToTranslucent(TranslucentConversionListener) + */ + public void onTranslucentConversionComplete(boolean drawComplete); + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a23611e..acfcb40 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1499,10 +1499,18 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case CONVERT_TO_OPAQUE_TRANSACTION: { + case CONVERT_FROM_TRANSLUCENT_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - convertToOpaque(token); + convertFromTranslucent(token); + reply.writeNoException(); + return true; + } + + case CONVERT_TO_TRANSLUCENT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + convertToTranslucent(token); reply.writeNoException(); return true; } @@ -1957,6 +1965,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case NOTIFY_ACTIVITY_DRAWN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + notifyActivityDrawn(token); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -3840,13 +3855,25 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } - public void convertToOpaque(IBinder token) + public void convertFromTranslucent(IBinder token) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(CONVERT_FROM_TRANSLUCENT_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void convertToTranslucent(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); - mRemote.transact(CONVERT_TO_OPAQUE_TRANSACTION, data, reply, 0); + mRemote.transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); @@ -4482,5 +4509,16 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public void notifyActivityDrawn(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(NOTIFY_ACTIVITY_DRAWN_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4568525..b24aeb0 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -16,8 +16,6 @@ package android.app; -import static android.view.DisplayAdjustments.DEVELOPMENT_RESOURCES_DEPEND_ON_ACTIVITY_TOKEN; - import android.app.backup.BackupAgent; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; @@ -77,7 +75,6 @@ import android.util.Log; import android.util.LogPrinter; import android.util.PrintWriterPrinter; import android.util.Slog; -import android.view.DisplayAdjustments; import android.view.Display; import android.view.HardwareRenderer; import android.view.View; @@ -150,7 +147,7 @@ public final class ActivityThread { public static final boolean DEBUG_BROADCAST = false; private static final boolean DEBUG_RESULTS = false; private static final boolean DEBUG_BACKUP = false; - private static final boolean DEBUG_CONFIGURATION = false; + public static final boolean DEBUG_CONFIGURATION = false; private static final boolean DEBUG_SERVICE = false; private static final boolean DEBUG_MEMORY_TRIM = false; private static final boolean DEBUG_PROVIDER = false; @@ -182,8 +179,6 @@ public final class ActivityThread { boolean mDensityCompatMode; Configuration mConfiguration; Configuration mCompatConfiguration; - Configuration mResConfiguration; - CompatibilityInfo mResCompatibilityInfo; Application mInitialApplication; final ArrayList<Application> mAllApplications = new ArrayList<Application>(); @@ -212,14 +207,12 @@ public final class ActivityThread { = new HashMap<String, WeakReference<LoadedApk>>(); final HashMap<String, WeakReference<LoadedApk>> mResourcePackages = new HashMap<String, WeakReference<LoadedApk>>(); - final HashMap<DisplayAdjustments, DisplayMetrics> mDefaultDisplayMetrics - = new HashMap<DisplayAdjustments, DisplayMetrics>(); - final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources - = new HashMap<ResourcesKey, WeakReference<Resources> >(); final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<ActivityClientRecord>(); Configuration mPendingConfiguration = null; + private final ResourcesManager mResourcesManager; + private static final class ProviderKey { final String authority; final int userId; @@ -555,7 +548,7 @@ public final class ActivityThread { private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; private void updatePendingConfiguration(Configuration config) { - synchronized (mPackages) { + synchronized (mResourcesManager) { if (mPendingConfiguration == null || mPendingConfiguration.isOtherSeqNewer(config)) { mPendingConfiguration = config; @@ -1222,6 +1215,9 @@ public final class ActivityThread { queueOrSendMessage(H.TRIM_MEMORY, null, level); } + public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) { + queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0); + } } private class H extends Handler { @@ -1269,6 +1265,7 @@ public final class ActivityThread { public static final int DUMP_PROVIDER = 141; public static final int UNSTABLE_PROVIDER_DIED = 142; public static final int REQUEST_ACTIVITY_EXTRAS = 143; + public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144; String codeToString(int code) { if (DEBUG_MESSAGES) { switch (code) { @@ -1316,6 +1313,7 @@ public final class ActivityThread { case DUMP_PROVIDER: return "DUMP_PROVIDER"; case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED"; case REQUEST_ACTIVITY_EXTRAS: return "REQUEST_ACTIVITY_EXTRAS"; + case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE"; } } return Integer.toString(code); @@ -1530,6 +1528,9 @@ public final class ActivityThread { case REQUEST_ACTIVITY_EXTRAS: handleRequestActivityExtras((RequestActivityExtras)msg.obj); break; + case TRANSLUCENT_CONVERSION_COMPLETE: + handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1); + break; } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); } @@ -1608,72 +1609,6 @@ public final class ActivityThread { } } - private static class ResourcesKey { - final private String mResDir; - final private int mDisplayId; - final private Configuration mOverrideConfiguration; - final private float mScale; - final private int mHash; - final private IBinder mToken; - - ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, - float scale, IBinder token) { - mResDir = resDir; - mDisplayId = displayId; - if (overrideConfiguration != null) { - if (Configuration.EMPTY.equals(overrideConfiguration)) { - overrideConfiguration = null; - } - } - mOverrideConfiguration = overrideConfiguration; - mScale = scale; - int hash = 17; - hash = 31 * hash + mResDir.hashCode(); - hash = 31 * hash + mDisplayId; - hash = 31 * hash + (mOverrideConfiguration != null - ? mOverrideConfiguration.hashCode() : 0); - hash = 31 * hash + Float.floatToIntBits(mScale); - if (DEVELOPMENT_RESOURCES_DEPEND_ON_ACTIVITY_TOKEN) { - mToken = token; - hash = 31 * hash + (mToken == null ? 0 : mToken.hashCode()); - } else { - mToken = null; - } - mHash = hash; - } - - @Override - public int hashCode() { - return mHash; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ResourcesKey)) { - return false; - } - ResourcesKey peer = (ResourcesKey) obj; - if (!mResDir.equals(peer.mResDir)) { - return false; - } - if (mDisplayId != peer.mDisplayId) { - return false; - } - if (mOverrideConfiguration != peer.mOverrideConfiguration) { - if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) { - return false; - } - if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) { - return false; - } - } - if (mScale != peer.mScale) { - return false; - } - return true; - } - } - public static ActivityThread currentActivityThread() { return sCurrentActivityThread; } @@ -1707,49 +1642,6 @@ public final class ActivityThread { return sPackageManager; } - private void flushDisplayMetricsLocked() { - mDefaultDisplayMetrics.clear(); - } - - DisplayMetrics getDisplayMetricsLocked(int displayId) { - return getDisplayMetricsLocked(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - } - - DisplayMetrics getDisplayMetricsLocked(int displayId, DisplayAdjustments daj) { - boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); - DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(daj) : null; - if (dm != null) { - return dm; - } - dm = new DisplayMetrics(); - - DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); - if (displayManager == null) { - // may be null early in system startup - dm.setToDefaults(); - return dm; - } - - if (isDefaultDisplay) { - mDefaultDisplayMetrics.put(daj, dm); - } - - Display d = displayManager.getCompatibleDisplay(displayId, daj); - if (d != null) { - d.getMetrics(dm); - } else { - // Display no longer exists - // FIXME: This would not be a problem if we kept the Display object around - // instead of using the raw display id everywhere. The Display object caches - // its information even after the display has been removed. - dm.setToDefaults(); - } - //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h=" - // + metrics.heightPixels + " den=" + metrics.density - // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi); - return dm; - } - private Configuration mMainThreadConfig = new Configuration(); Configuration applyConfigCompatMainThread(int displayDensity, Configuration config, CompatibilityInfo compat) { @@ -1765,90 +1657,12 @@ public final class ActivityThread { } /** - * Creates the top level Resources for applications with the given compatibility info. - * - * @param resDir the resource directory. - * @param compatInfo the compability info. Must not be null. - * @param token the application token for determining stack bounds. - */ - Resources getTopLevelResources(String resDir, int displayId, - Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { - final float scale = compatInfo.applicationScale; - ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, - token); - Resources r; - synchronized (mPackages) { - // Resources is app scale dependent. - if (false) { - Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); - } - WeakReference<Resources> wr = mActiveResources.get(key); - r = wr != null ? wr.get() : null; - //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate()); - if (r != null && r.getAssets().isUpToDate()) { - if (false) { - Slog.w(TAG, "Returning cached resources " + r + " " + resDir - + ": appScale=" + r.getCompatibilityInfo().applicationScale); - } - return r; - } - } - - //if (r != null) { - // Slog.w(TAG, "Throwing away out-of-date resources!!!! " - // + r + " " + resDir); - //} - - AssetManager assets = new AssetManager(); - if (assets.addAssetPath(resDir) == 0) { - return null; - } - - //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); - DisplayMetrics dm = getDisplayMetricsLocked(displayId); - Configuration config; - boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); - if (!isDefaultDisplay || key.mOverrideConfiguration != null) { - config = new Configuration(getConfiguration()); - if (!isDefaultDisplay) { - applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config); - } - if (key.mOverrideConfiguration != null) { - config.updateFrom(key.mOverrideConfiguration); - } - } else { - config = getConfiguration(); - } - r = new Resources(assets, dm, config, compatInfo, token); - if (false) { - Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " - + r.getConfiguration() + " appScale=" - + r.getCompatibilityInfo().applicationScale); - } - - synchronized (mPackages) { - WeakReference<Resources> wr = mActiveResources.get(key); - Resources existing = wr != null ? wr.get() : null; - if (existing != null && existing.getAssets().isUpToDate()) { - // Someone else already created the resources while we were - // unlocked; go ahead and use theirs. - r.getAssets().close(); - return existing; - } - - // XXX need to remove entries when weak references go away - mActiveResources.put(key, new WeakReference<Resources>(r)); - return r; - } - } - - /** * Creates the top level resources for the given package. */ Resources getTopLevelResources(String resDir, int displayId, Configuration overrideConfiguration, LoadedApk pkgInfo) { - return getTopLevelResources(resDir, displayId, overrideConfiguration, + return mResourcesManager.getTopLevelResources(resDir, displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null); } @@ -1863,7 +1677,7 @@ public final class ActivityThread { public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, int flags, int userId) { - synchronized (mPackages) { + synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) { ref = mPackages.get(packageName); @@ -1933,7 +1747,7 @@ public final class ActivityThread { } public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) { - synchronized (mPackages) { + synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; if (includeCode) { ref = mPackages.get(packageName); @@ -1946,7 +1760,7 @@ public final class ActivityThread { private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { - synchronized (mPackages) { + synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; if (includeCode) { ref = mPackages.get(aInfo.packageName); @@ -1978,6 +1792,7 @@ public final class ActivityThread { } ActivityThread() { + mResourcesManager = ResourcesManager.getInstance(); } public ApplicationThread getApplicationThread() @@ -1990,10 +1805,6 @@ public final class ActivityThread { return mInstrumentation; } - public Configuration getConfiguration() { - return mResConfiguration; - } - public boolean isProfiling() { return mProfiler != null && mProfiler.profileFile != null && mProfiler.profileFd == null; @@ -2023,8 +1834,8 @@ public final class ActivityThread { LoadedApk info = new LoadedApk(this, "android", context, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO); context.init(info, null, this); - context.getResources().updateConfiguration(getConfiguration(), - getDisplayMetricsLocked(Display.DEFAULT_DISPLAY)); + context.getResources().updateConfiguration(mResourcesManager.getConfiguration(), + mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY)); mSystemContext = context; //Slog.i(TAG, "Created system resources " + context.getResources() // + ": " + context.getResources().getConfiguration()); @@ -2452,7 +2263,14 @@ public final class ActivityThread { } catch (RemoteException e) { } } - + + public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) { + ActivityClientRecord r = mActivities.get(token); + if (r != null) { + r.activity.onTranslucentConversionComplete(drawComplete); + } + } + private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>(); /** @@ -3419,7 +3237,7 @@ public final class ActivityThread { } private void handleSetCoreSettings(Bundle coreSettings) { - synchronized (mPackages) { + synchronized (mResourcesManager) { mCoreSettings = coreSettings; } } @@ -3509,7 +3327,7 @@ public final class ActivityThread { private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance) { ActivityClientRecord r = mActivities.get(token); - Class activityClass = null; + Class<? extends Activity> activityClass = null; if (localLOGV) Slog.v(TAG, "Performing finish of " + r); if (r != null) { activityClass = r.activity.getClass(); @@ -3664,7 +3482,7 @@ public final class ActivityThread { boolean fromServer) { ActivityClientRecord target = null; - synchronized (mPackages) { + synchronized (mResourcesManager) { for (int i=0; i<mRelaunchingActivities.size(); i++) { ActivityClientRecord r = mRelaunchingActivities.get(i); if (r.token == token) { @@ -3725,7 +3543,7 @@ public final class ActivityThread { // First: make sure we have the most recent configuration and most // recent version of the activity, or skip it if some previous call // had taken a more recent version. - synchronized (mPackages) { + synchronized (mResourcesManager) { int N = mRelaunchingActivities.size(); IBinder token = tmp.token; tmp = null; @@ -3854,7 +3672,7 @@ public final class ActivityThread { ArrayList<ComponentCallbacks2> callbacks = new ArrayList<ComponentCallbacks2>(); - synchronized (mPackages) { + synchronized (mResourcesManager) { final int N = mAllApplications.size(); for (int i=0; i<N; i++) { callbacks.add(mAllApplications.get(i)); @@ -3948,114 +3766,18 @@ public final class ActivityThread { } public final void applyConfigurationToResources(Configuration config) { - synchronized (mPackages) { - applyConfigurationToResourcesLocked(config, null); + synchronized (mResourcesManager) { + mResourcesManager.applyConfigurationToResourcesLocked(config, null); } } - final boolean applyConfigurationToResourcesLocked(Configuration config, - CompatibilityInfo compat) { - if (mResConfiguration == null) { - mResConfiguration = new Configuration(); - } - if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq=" - + mResConfiguration.seq + ", newSeq=" + config.seq); - return false; - } - int changes = mResConfiguration.updateFrom(config); - flushDisplayMetricsLocked(); - DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(Display.DEFAULT_DISPLAY); - - if (compat != null && (mResCompatibilityInfo == null || - !mResCompatibilityInfo.equals(compat))) { - mResCompatibilityInfo = compat; - changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT - | ActivityInfo.CONFIG_SCREEN_SIZE - | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; - } - - // set it for java, this also affects newly created Resources - if (config.locale != null) { - Locale.setDefault(config.locale); - } - - Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat); - - ApplicationPackageManager.configurationChanged(); - //Slog.i(TAG, "Configuration changed in " + currentPackageName()); - - Configuration tmpConfig = null; - - Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it = - mActiveResources.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next(); - Resources r = entry.getValue().get(); - if (r != null) { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " - + r + " config to: " + config); - int displayId = entry.getKey().mDisplayId; - boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); - DisplayMetrics dm = defaultDisplayMetrics; - Configuration overrideConfig = entry.getKey().mOverrideConfiguration; - if (!isDefaultDisplay || overrideConfig != null) { - if (tmpConfig == null) { - tmpConfig = new Configuration(); - } - tmpConfig.setTo(config); - if (!isDefaultDisplay) { - dm = getDisplayMetricsLocked(displayId); - applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig); - } - if (overrideConfig != null) { - tmpConfig.updateFrom(overrideConfig); - } - r.updateConfiguration(tmpConfig, dm, compat); - } else { - r.updateConfiguration(config, dm, compat); - } - //Slog.i(TAG, "Updated app resources " + v.getKey() - // + " " + r + ": " + r.getConfiguration()); - } else { - //Slog.i(TAG, "Removing old resources " + v.getKey()); - it.remove(); - } - } - - return changes != 0; - } - - final void applyNonDefaultDisplayMetricsToConfigurationLocked( - DisplayMetrics dm, Configuration config) { - config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; - config.densityDpi = dm.densityDpi; - config.screenWidthDp = (int)(dm.widthPixels / dm.density); - config.screenHeightDp = (int)(dm.heightPixels / dm.density); - int sl = Configuration.resetScreenLayout(config.screenLayout); - if (dm.widthPixels > dm.heightPixels) { - config.orientation = Configuration.ORIENTATION_LANDSCAPE; - config.screenLayout = Configuration.reduceScreenLayout(sl, - config.screenWidthDp, config.screenHeightDp); - } else { - config.orientation = Configuration.ORIENTATION_PORTRAIT; - config.screenLayout = Configuration.reduceScreenLayout(sl, - config.screenHeightDp, config.screenWidthDp); - } - config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate - config.compatScreenWidthDp = config.screenWidthDp; - config.compatScreenHeightDp = config.screenHeightDp; - config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp; - } - final Configuration applyCompatConfiguration(int displayDensity) { Configuration config = mConfiguration; if (mCompatConfiguration == null) { mCompatConfiguration = new Configuration(); } mCompatConfiguration.setTo(mConfiguration); - if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) { - mResCompatibilityInfo.applyToConfiguration(displayDensity, mCompatConfiguration); + if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) { config = mCompatConfiguration; } return config; @@ -4065,7 +3787,7 @@ public final class ActivityThread { int configDiff = 0; - synchronized (mPackages) { + synchronized (mResourcesManager) { if (mPendingConfiguration != null) { if (!mPendingConfiguration.isOtherSeqNewer(config)) { config = mPendingConfiguration; @@ -4081,9 +3803,9 @@ public final class ActivityThread { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: " + config); - - applyConfigurationToResourcesLocked(config, compat); - + + mResourcesManager.applyConfigurationToResourcesLocked(config, compat); + if (mConfiguration == null) { mConfiguration = new Configuration(); } @@ -4333,7 +4055,7 @@ public final class ActivityThread { * reflect configuration changes. The configuration object passed * in AppBindData can be safely assumed to be up to date */ - applyConfigurationToResourcesLocked(data.config, data.compatInfo); + mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo); mCurDefaultDisplayDpi = data.config.densityDpi; applyCompatConfiguration(mCurDefaultDisplayDpi); @@ -5045,6 +4767,7 @@ public final class ActivityThread { mSystemThread = system; if (!system) { ViewRootImpl.addFirstDrawHandler(new Runnable() { + @Override public void run() { ensureJitEnabled(); } @@ -5081,12 +4804,13 @@ public final class ActivityThread { DropBox.setReporter(new DropBoxReporter()); ViewRootImpl.addConfigCallback(new ComponentCallbacks2() { + @Override public void onConfigurationChanged(Configuration newConfig) { - synchronized (mPackages) { + synchronized (mResourcesManager) { // We need to apply this change to the resources // immediately, because upon returning the view // hierarchy will be informed about it. - if (applyConfigurationToResourcesLocked(newConfig, null)) { + if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) { // This actually changed the resources! Tell // everyone about it. if (mPendingConfiguration == null || @@ -5098,8 +4822,10 @@ public final class ActivityThread { } } } + @Override public void onLowMemory() { } + @Override public void onTrimMemory(int level) { } }); @@ -5119,12 +4845,11 @@ public final class ActivityThread { } public int getIntCoreSetting(String key, int defaultValue) { - synchronized (mPackages) { + synchronized (mResourcesManager) { if (mCoreSettings != null) { return mCoreSettings.getInt(key, defaultValue); - } else { - return defaultValue; } + return defaultValue; } } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 32bb71e..de94bb7 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -33,21 +33,22 @@ import android.os.RemoteException; /** * API for interacting with "application operation" tracking. Allows you to: * - * - Note when operations are happening, and find out if they are allowed for the current caller. - * - Disallow specific apps from doing specific operations. - * - Collect all of the current information about operations that have been executed or are not - * being allowed. - * - Monitor for changes in whether an operation is allowed. + * <ul> + * <li> Note when operations are happening, and find out if they are allowed for the current + * caller.</li> + * <li> Disallow specific apps from doing specific operations.</li> + * <li> Collect all of the current information about operations that have been executed or are not + * being allowed.</li> + * <li> Monitor for changes in whether an operation is allowed.</li> + * </ul> * - * Each operation is identified by a single integer; these integers are a fixed set of + * <p>Each operation is identified by a single integer; these integers are a fixed set of * operations, enumerated by the OP_* constants. * - * When checking operations, the result is a "mode" integer indicating the current setting + * <p></p>When checking operations, the result is a "mode" integer indicating the current setting * for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but * fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a * SecurityException back to the caller; the normal operation calls will do this for you). - * - * @hide */ public class AppOpsManager { final Context mContext; @@ -63,50 +64,95 @@ public class AppOpsManager { // - increment _NUM_OP // - add rows to sOpToSwitch, sOpNames, sOpPerms // - add descriptive strings to Settings/res/values/arrays.xml + + /** No operation specified. */ public static final int OP_NONE = -1; + /** Access to coarse location information. */ public static final int OP_COARSE_LOCATION = 0; + /** Access to fine location information. */ public static final int OP_FINE_LOCATION = 1; + /** Causing GPS to run. */ public static final int OP_GPS = 2; + /** @hide */ public static final int OP_VIBRATE = 3; + /** @hide */ public static final int OP_READ_CONTACTS = 4; + /** @hide */ public static final int OP_WRITE_CONTACTS = 5; + /** @hide */ public static final int OP_READ_CALL_LOG = 6; + /** @hide */ public static final int OP_WRITE_CALL_LOG = 7; + /** @hide */ public static final int OP_READ_CALENDAR = 8; + /** @hide */ public static final int OP_WRITE_CALENDAR = 9; + /** @hide */ public static final int OP_WIFI_SCAN = 10; + /** @hide */ public static final int OP_POST_NOTIFICATION = 11; + /** @hide */ public static final int OP_NEIGHBORING_CELLS = 12; + /** @hide */ public static final int OP_CALL_PHONE = 13; + /** @hide */ public static final int OP_READ_SMS = 14; + /** @hide */ public static final int OP_WRITE_SMS = 15; + /** @hide */ public static final int OP_RECEIVE_SMS = 16; + /** @hide */ public static final int OP_RECEIVE_EMERGECY_SMS = 17; + /** @hide */ public static final int OP_RECEIVE_MMS = 18; + /** @hide */ public static final int OP_RECEIVE_WAP_PUSH = 19; + /** @hide */ public static final int OP_SEND_SMS = 20; + /** @hide */ public static final int OP_READ_ICC_SMS = 21; + /** @hide */ public static final int OP_WRITE_ICC_SMS = 22; + /** @hide */ public static final int OP_WRITE_SETTINGS = 23; + /** @hide */ public static final int OP_SYSTEM_ALERT_WINDOW = 24; + /** @hide */ public static final int OP_ACCESS_NOTIFICATIONS = 25; + /** @hide */ public static final int OP_CAMERA = 26; + /** @hide */ public static final int OP_RECORD_AUDIO = 27; + /** @hide */ public static final int OP_PLAY_AUDIO = 28; + /** @hide */ public static final int OP_READ_CLIPBOARD = 29; + /** @hide */ public static final int OP_WRITE_CLIPBOARD = 30; + /** @hide */ public static final int OP_TAKE_MEDIA_BUTTONS = 31; + /** @hide */ public static final int OP_TAKE_AUDIO_FOCUS = 32; + /** @hide */ public static final int OP_AUDIO_MASTER_VOLUME = 33; + /** @hide */ public static final int OP_AUDIO_VOICE_VOLUME = 34; + /** @hide */ public static final int OP_AUDIO_RING_VOLUME = 35; + /** @hide */ public static final int OP_AUDIO_MEDIA_VOLUME = 36; + /** @hide */ public static final int OP_AUDIO_ALARM_VOLUME = 37; + /** @hide */ public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38; + /** @hide */ public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39; + /** @hide */ public static final int OP_WAKE_LOCK = 40; + /** Continually monitoring location data. */ + public static final int OP_MONITOR_LOCATION = 41; /** @hide */ - public static final int _NUM_OP = 41; + public static final int _NUM_OP = 42; /** * This maps each operation to the operation that serves as the @@ -158,6 +204,7 @@ public class AppOpsManager { OP_AUDIO_NOTIFICATION_VOLUME, OP_AUDIO_BLUETOOTH_VOLUME, OP_WAKE_LOCK, + OP_COARSE_LOCATION, }; /** @@ -206,6 +253,7 @@ public class AppOpsManager { "AUDIO_NOTIFICATION_VOLUME", "AUDIO_BLUETOOTH_VOLUME", "WAKE_LOCK", + "MONITOR_LOCATION", }; /** @@ -254,10 +302,12 @@ public class AppOpsManager { null, // no permission for changing notification volume null, // no permission for changing bluetooth volume android.Manifest.permission.WAKE_LOCK, + null, // no permission for generic location monitoring }; /** * Retrieve the op switch that controls the given operation. + * @hide */ public static int opToSwitch(int op) { return sOpToSwitch[op]; @@ -273,6 +323,7 @@ public class AppOpsManager { /** * Retrieve the permission associated with an operation, or null if there is not one. + * @hide */ public static String opToPermission(int op) { return sOpPerms[op]; @@ -280,6 +331,7 @@ public class AppOpsManager { /** * Class holding all of the operation information associated with an app. + * @hide */ public static class PackageOps implements Parcelable { private final String mPackageName; @@ -342,6 +394,7 @@ public class AppOpsManager { /** * Class holding the information about one unique operation of an application. + * @hide */ public static class OpEntry implements Parcelable { private final int mOp; @@ -422,7 +475,7 @@ public class AppOpsManager { public void opChanged(int op, String packageName); } - public AppOpsManager(Context context, IAppOpsService service) { + AppOpsManager(Context context, IAppOpsService service) { mContext = context; mService = service; } @@ -431,6 +484,7 @@ public class AppOpsManager { * Retrieve current operation state for all applications. * * @param ops The set of operations you are interested in, or null if you want all of them. + * @hide */ public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) { try { @@ -446,6 +500,7 @@ public class AppOpsManager { * @param uid The uid of the application of interest. * @param packageName The name of the application of interest. * @param ops The set of operations you are interested in, or null if you want all of them. + * @hide */ public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) { try { @@ -455,6 +510,7 @@ public class AppOpsManager { return null; } + /** @hide */ public void setMode(int code, int uid, String packageName, int mode) { try { mService.setMode(code, uid, packageName, mode); @@ -462,6 +518,12 @@ public class AppOpsManager { } } + /** + * Monitor for changes to the operating mode for the given op in the given app package. + * @param op The operation to monitor, one of OP_*. + * @param packageName The name of the application to monitor. + * @param callback Where to report changes. + */ public void startWatchingMode(int op, String packageName, final Callback callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); @@ -480,6 +542,10 @@ public class AppOpsManager { } } + /** + * Stop monitoring that was previously started with {@link #startWatchingMode}. All + * monitoring associated with this callback will be removed. + */ public void stopWatchingMode(Callback callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); @@ -492,6 +558,22 @@ public class AppOpsManager { } } + /** + * Do a quick check for whether an application might be able to perform an operation. + * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)} + * or {@link #startOp(int, int, String)} for your actual security checks, which also + * ensure that the given uid and package name are consistent. This function can just be + * used for a quick check to see if an operation has been disabled for the application, + * as an early reject of some work. This does not modify the time stamp or other data + * about the operation. + * @param op The operation to check. One of the OP_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. + */ public int checkOp(int op, int uid, String packageName) { try { int mode = mService.checkOperation(op, uid, packageName); @@ -504,6 +586,10 @@ public class AppOpsManager { return MODE_IGNORED; } + /** + * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it + * returns {@link #MODE_ERRORED}. + */ public int checkOpNoThrow(int op, int uid, String packageName) { try { return mService.checkOperation(op, uid, packageName); @@ -512,6 +598,20 @@ public class AppOpsManager { return MODE_IGNORED; } + /** + * Make note of an application performing an operation. Note that you must pass + * in both the uid and name of the application to be checked; this function will verify + * that these two match, and if not, return {@link #MODE_IGNORED}. If this call + * succeeds, the last execution time of the operation for this app will be updated to + * the current time. + * @param op The operation to note. One of the OP_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. + */ public int noteOp(int op, int uid, String packageName) { try { int mode = mService.noteOperation(op, uid, packageName); @@ -524,6 +624,10 @@ public class AppOpsManager { return MODE_IGNORED; } + /** + * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it + * returns {@link #MODE_ERRORED}. + */ public int noteOpNoThrow(int op, int uid, String packageName) { try { return mService.noteOperation(op, uid, packageName); @@ -532,10 +636,27 @@ public class AppOpsManager { return MODE_IGNORED; } + /** @hide */ public int noteOp(int op) { return noteOp(op, Process.myUid(), mContext.getBasePackageName()); } + /** + * Report that an application has started executing a long-running operation. Note that you + * must pass in both the uid and name of the application to be checked; this function will + * verify that these two match, and if not, return {@link #MODE_IGNORED}. If this call + * succeeds, the last execution time of the operation for this app will be updated to + * the current time and the operation will be marked as "running". In this case you must + * later call {@link #finishOp(int, int, String)} to report when the application is no + * longer performing the operation. + * @param op The operation to start. One of the OP_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. + */ public int startOp(int op, int uid, String packageName) { try { int mode = mService.startOperation(op, uid, packageName); @@ -548,6 +669,10 @@ public class AppOpsManager { return MODE_IGNORED; } + /** + * Like {@link #startOp} but instead of throwing a {@link SecurityException} it + * returns {@link #MODE_ERRORED}. + */ public int startOpNoThrow(int op, int uid, String packageName) { try { return mService.startOperation(op, uid, packageName); @@ -556,10 +681,17 @@ public class AppOpsManager { return MODE_IGNORED; } + /** @hide */ public int startOp(int op) { return startOp(op, Process.myUid(), mContext.getBasePackageName()); } + /** + * Report that an application is no longer performing an operation that had previously + * been started with {@link #startOp(int, int, String)}. There is no validation of input + * or result; the parameters supplied here must be the exact same ones previously passed + * in when starting the operation. + */ public void finishOp(int op, int uid, String packageName) { try { mService.finishOperation(op, uid, packageName); diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index e903447..cc495aa 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -603,6 +603,16 @@ public abstract class ApplicationThreadNative extends Binder reply.writeNoException(); return true; } + + case SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + IBinder token = data.readStrongBinder(); + boolean timeout = data.readInt() == 1; + scheduleTranslucentConversionComplete(token, timeout); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1197,6 +1207,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + @Override public void unstableProviderDied(IBinder provider) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -1205,6 +1216,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + @Override public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType) throws RemoteException { Parcel data = Parcel.obtain(); @@ -1215,4 +1227,15 @@ class ApplicationThreadProxy implements IApplicationThread { mRemote.transact(REQUEST_ACTIVITY_EXTRAS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } + + @Override + public void scheduleTranslucentConversionComplete(IBinder token, boolean timeout) + throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeStrongBinder(token); + data.writeInt(timeout ? 1 : 0); + mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index a5106e4..6a0fbd5 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -192,6 +192,7 @@ class ContextImpl extends Context { private Context mReceiverRestrictedContext = null; private boolean mRestricted; private UserHandle mUser; + private ResourcesManager mResourcesManager; private final Object mSync = new Object(); @@ -1832,8 +1833,9 @@ class ContextImpl extends Context { ContextImpl c = new ContextImpl(); c.init(mPackageInfo, null, mMainThread); - c.mResources = mMainThread.getTopLevelResources(mPackageInfo.getResDir(), getDisplayId(), - overrideConfiguration, mResources.getCompatibilityInfo(), mActivityToken); + c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), + getDisplayId(), overrideConfiguration, mResources.getCompatibilityInfo(), + mActivityToken); return c; } @@ -1849,8 +1851,8 @@ class ContextImpl extends Context { context.init(mPackageInfo, null, mMainThread); context.mDisplay = display; DisplayAdjustments daj = getDisplayAdjustments(displayId); - context.mResources = mMainThread.getTopLevelResources(mPackageInfo.getResDir(), displayId, - null, daj.getCompatibilityInfo(), null); + context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), + displayId, null, daj.getCompatibilityInfo(), null); return context; } @@ -1929,6 +1931,7 @@ class ContextImpl extends Context { mPackageInfo = packageInfo; mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName; mResources = mPackageInfo.getResources(mainThread); + mResourcesManager = ResourcesManager.getInstance(); CompatibilityInfo compatInfo = container == null ? null : container.getCompatibilityInfo(); @@ -1945,7 +1948,7 @@ class ContextImpl extends Context { } mDisplayAdjustments.setCompatibilityInfo(compatInfo); mDisplayAdjustments.setActivityToken(activityToken); - mResources = mainThread.getTopLevelResources(mPackageInfo.getResDir(), + mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY, null, compatInfo, activityToken); } else { mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo()); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 3793c73..19858dc 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -258,7 +258,7 @@ public interface IActivityManager extends IInterface { StrictMode.ViolationInfo crashInfo) throws RemoteException; /* - * This will deliver the specified signal to all the persistent processes. Currently only + * This will deliver the specified signal to all the persistent processes. Currently only * SIGUSR1 is delivered. All others are ignored. */ public void signalPersistentProcesses(int signal) throws RemoteException; @@ -301,7 +301,9 @@ public interface IActivityManager extends IInterface { public void finishHeavyWeightApp() throws RemoteException; - public void convertToOpaque(IBinder token) throws RemoteException; + public void convertFromTranslucent(IBinder token) throws RemoteException; + public void convertToTranslucent(IBinder token) throws RemoteException; + public void notifyActivityDrawn(IBinder token) throws RemoteException; public void setImmersive(IBinder token, boolean immersive) throws RemoteException; public boolean isImmersive(IBinder token) throws RemoteException; @@ -494,7 +496,7 @@ public interface IActivityManager extends IInterface { thisTime = source.readLong(); totalTime = source.readLong(); } - }; + } String descriptor = "android.app.IActivityManager"; @@ -670,6 +672,8 @@ public interface IActivityManager extends IInterface { int GET_STACK_BOXES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170; int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171; int GET_STACK_BOX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172; - int CONVERT_TO_OPAQUE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173; - int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174; + int CONVERT_FROM_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173; + int CONVERT_TO_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174; + int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175; + int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index a009bd3..286566d 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -133,6 +133,8 @@ public interface IApplicationThread extends IInterface { void unstableProviderDied(IBinder provider) throws RemoteException; void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType) throws RemoteException; + void scheduleTranslucentConversionComplete(IBinder token, boolean timeout) + throws RemoteException; String descriptor = "android.app.IApplicationThread"; @@ -183,4 +185,5 @@ public interface IApplicationThread extends IInterface { int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45; int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46; int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47; + int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48; } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 573a6aa..05d3a47 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -138,11 +138,11 @@ public final class LoadedApk { if (ActivityThread.mSystemContext == null) { ActivityThread.mSystemContext = ContextImpl.createSystemContext(mainThread); + ResourcesManager resourcesManager = ResourcesManager.getInstance(); ActivityThread.mSystemContext.getResources().updateConfiguration( - mainThread.getConfiguration(), - mainThread.getDisplayMetricsLocked( - Display.DEFAULT_DISPLAY, mDisplayAdjustments), - compatInfo); + resourcesManager.getConfiguration(), + resourcesManager.getDisplayMetricsLocked( + Display.DEFAULT_DISPLAY, mDisplayAdjustments), compatInfo); //Slog.i(TAG, "Created system resources " // + mSystemContext.getResources() + ": " // + mSystemContext.getResources().getConfiguration()); diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java new file mode 100644 index 0000000..e9693dd --- /dev/null +++ b/core/java/android/app/ResourcesManager.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static android.app.ActivityThread.DEBUG_CONFIGURATION; + +import android.app.ApplicationPackageManager; +import android.content.pm.ActivityInfo; +import android.content.res.AssetManager; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.ResourcesKey; +import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.util.ArrayMap; +import android.util.DisplayMetrics; +import android.util.Slog; +import android.view.Display; +import android.view.DisplayAdjustments; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +/** @hide */ +public class ResourcesManager { + static final String TAG = "ResourcesManager"; + static final boolean DEBUG_CACHE = false; + static final boolean DEBUG_STATS = true; + + private static ResourcesManager sResourcesManager; + final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources + = new HashMap<ResourcesKey, WeakReference<Resources> >(); + + final ArrayMap<DisplayAdjustments, DisplayMetrics> mDefaultDisplayMetrics + = new ArrayMap<DisplayAdjustments, DisplayMetrics>(); + + CompatibilityInfo mResCompatibilityInfo; + + Configuration mResConfiguration; + final Configuration mTmpConfig = new Configuration(); + + public static ResourcesManager getInstance() { + synchronized (ResourcesManager.class) { + if (sResourcesManager == null) { + sResourcesManager = new ResourcesManager(); + } + return sResourcesManager; + } + } + + public Configuration getConfiguration() { + return mResConfiguration; + } + + public void flushDisplayMetricsLocked() { + mDefaultDisplayMetrics.clear(); + } + + public DisplayMetrics getDisplayMetricsLocked(int displayId) { + return getDisplayMetricsLocked(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); + } + + public DisplayMetrics getDisplayMetricsLocked(int displayId, DisplayAdjustments daj) { + boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); + DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(daj) : null; + if (dm != null) { + return dm; + } + dm = new DisplayMetrics(); + + DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); + if (displayManager == null) { + // may be null early in system startup + dm.setToDefaults(); + return dm; + } + + if (isDefaultDisplay) { + mDefaultDisplayMetrics.put(daj, dm); + } + + Display d = displayManager.getCompatibleDisplay(displayId, daj); + if (d != null) { + d.getMetrics(dm); + } else { + // Display no longer exists + // FIXME: This would not be a problem if we kept the Display object around + // instead of using the raw display id everywhere. The Display object caches + // its information even after the display has been removed. + dm.setToDefaults(); + } + //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h=" + // + metrics.heightPixels + " den=" + metrics.density + // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi); + return dm; + } + + final void applyNonDefaultDisplayMetricsToConfigurationLocked( + DisplayMetrics dm, Configuration config) { + config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; + config.densityDpi = dm.densityDpi; + config.screenWidthDp = (int)(dm.widthPixels / dm.density); + config.screenHeightDp = (int)(dm.heightPixels / dm.density); + int sl = Configuration.resetScreenLayout(config.screenLayout); + if (dm.widthPixels > dm.heightPixels) { + config.orientation = Configuration.ORIENTATION_LANDSCAPE; + config.screenLayout = Configuration.reduceScreenLayout(sl, + config.screenWidthDp, config.screenHeightDp); + } else { + config.orientation = Configuration.ORIENTATION_PORTRAIT; + config.screenLayout = Configuration.reduceScreenLayout(sl, + config.screenHeightDp, config.screenWidthDp); + } + config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate + config.compatScreenWidthDp = config.screenWidthDp; + config.compatScreenHeightDp = config.screenHeightDp; + config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp; + } + + public boolean applyCompatConfiguration(int displayDensity, + Configuration compatConfiguration) { + if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) { + mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration); + return true; + } + return false; + } + + /** + * Creates the top level Resources for applications with the given compatibility info. + * + * @param resDir the resource directory. + * @param compatInfo the compability info. Must not be null. + * @param token the application token for determining stack bounds. + */ + public Resources getTopLevelResources(String resDir, int displayId, + Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { + final float scale = compatInfo.applicationScale; + ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, + token); + Resources r; + synchronized (this) { + // Resources is app scale dependent. + if (false) { + Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); + } + WeakReference<Resources> wr = mActiveResources.get(key); + r = wr != null ? wr.get() : null; + //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate()); + if (r != null && r.getAssets().isUpToDate()) { + if (false) { + Slog.w(TAG, "Returning cached resources " + r + " " + resDir + + ": appScale=" + r.getCompatibilityInfo().applicationScale); + } + return r; + } + } + + //if (r != null) { + // Slog.w(TAG, "Throwing away out-of-date resources!!!! " + // + r + " " + resDir); + //} + + AssetManager assets = new AssetManager(); + if (assets.addAssetPath(resDir) == 0) { + return null; + } + + //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); + DisplayMetrics dm = getDisplayMetricsLocked(displayId); + Configuration config; + boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); + final boolean hasOverrideConfig = key.hasOverrideConfiguration(); + if (!isDefaultDisplay || hasOverrideConfig) { + config = new Configuration(getConfiguration()); + if (!isDefaultDisplay) { + applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config); + } + if (hasOverrideConfig) { + config.updateFrom(key.mOverrideConfiguration); + } + } else { + config = getConfiguration(); + } + r = new Resources(assets, dm, config, compatInfo, token); + if (false) { + Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " + + r.getConfiguration() + " appScale=" + + r.getCompatibilityInfo().applicationScale); + } + + synchronized (this) { + WeakReference<Resources> wr = mActiveResources.get(key); + Resources existing = wr != null ? wr.get() : null; + if (existing != null && existing.getAssets().isUpToDate()) { + // Someone else already created the resources while we were + // unlocked; go ahead and use theirs. + r.getAssets().close(); + return existing; + } + + // XXX need to remove entries when weak references go away + mActiveResources.put(key, new WeakReference<Resources>(r)); + return r; + } + } + + public final boolean applyConfigurationToResourcesLocked(Configuration config, + CompatibilityInfo compat) { + if (mResConfiguration == null) { + mResConfiguration = new Configuration(); + } + if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) { + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq=" + + mResConfiguration.seq + ", newSeq=" + config.seq); + return false; + } + int changes = mResConfiguration.updateFrom(config); + flushDisplayMetricsLocked(); + DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(Display.DEFAULT_DISPLAY); + + if (compat != null && (mResCompatibilityInfo == null || + !mResCompatibilityInfo.equals(compat))) { + mResCompatibilityInfo = compat; + changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT + | ActivityInfo.CONFIG_SCREEN_SIZE + | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; + } + + // set it for java, this also affects newly created Resources + if (config.locale != null) { + Locale.setDefault(config.locale); + } + + Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat); + + ApplicationPackageManager.configurationChanged(); + //Slog.i(TAG, "Configuration changed in " + currentPackageName()); + + Configuration tmpConfig = null; + + Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it = + mActiveResources.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next(); + Resources r = entry.getValue().get(); + if (r != null) { + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " + + r + " config to: " + config); + int displayId = entry.getKey().mDisplayId; + boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); + DisplayMetrics dm = defaultDisplayMetrics; + ResourcesKey resourcesKey = entry.getKey(); + final boolean hasOverrideConfiguration = resourcesKey.hasOverrideConfiguration(); + if (!isDefaultDisplay || hasOverrideConfiguration) { + if (tmpConfig == null) { + tmpConfig = new Configuration(); + } + tmpConfig.setTo(config); + if (!isDefaultDisplay) { + dm = getDisplayMetricsLocked(displayId); + applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig); + } + if (hasOverrideConfiguration) { + tmpConfig.updateFrom(resourcesKey.mOverrideConfiguration); + } + r.updateConfiguration(tmpConfig, dm, compat); + } else { + r.updateConfiguration(config, dm, compat); + } + //Slog.i(TAG, "Updated app resources " + v.getKey() + // + " " + r + ": " + r.getConfiguration()); + } else { + //Slog.i(TAG, "Removing old resources " + v.getKey()); + it.remove(); + } + } + + return changes != 0; + } + +} diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index c24e0ee..6483cd9 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1668,13 +1668,6 @@ public class Resources { } /** - * @hide - */ - public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) { - updateSystemConfiguration(config, metrics, null); - } - - /** * Return the current display metrics that are in effect for this resource * object. The returned object should be treated as read-only. * diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java new file mode 100644 index 0000000..53e0f2c --- /dev/null +++ b/core/java/android/content/res/ResourcesKey.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.res; + +import android.os.IBinder; + +/** @hide */ +public final class ResourcesKey { + final String mResDir; + final float mScale; + private final int mHash; + private final IBinder mToken; + + public final int mDisplayId; + public final Configuration mOverrideConfiguration = new Configuration(); + + public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, + float scale, IBinder token) { + mResDir = resDir; + mDisplayId = displayId; + if (overrideConfiguration != null) { + mOverrideConfiguration.setTo(overrideConfiguration); + } + mScale = scale; + mToken = token; + + int hash = 17; + hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode()); + hash = 31 * hash + mDisplayId; + hash = 31 * hash + (mOverrideConfiguration != null + ? mOverrideConfiguration.hashCode() : 0); + hash = 31 * hash + Float.floatToIntBits(mScale); + mHash = hash; + } + + public boolean hasOverrideConfiguration() { + return !Configuration.EMPTY.equals(mOverrideConfiguration); + } + + @Override + public int hashCode() { + return mHash; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ResourcesKey)) { + return false; + } + ResourcesKey peer = (ResourcesKey) obj; + if (!mResDir.equals(peer.mResDir)) { + return false; + } + if (mDisplayId != peer.mDisplayId) { + return false; + } + if (mOverrideConfiguration != peer.mOverrideConfiguration) { + if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) { + return false; + } + if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) { + return false; + } + } + if (mScale != peer.mScale) { + return false; + } + return true; + } + + @Override + public String toString() { + return Integer.toHexString(mHash); + } +} diff --git a/core/java/android/hardware/photography/CameraMetadata.java b/core/java/android/hardware/photography/CameraMetadata.java index 5488952..1988967 100644 --- a/core/java/android/hardware/photography/CameraMetadata.java +++ b/core/java/android/hardware/photography/CameraMetadata.java @@ -20,7 +20,18 @@ import android.os.Parcelable; import android.os.Parcel; import android.util.Log; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; /** @@ -72,9 +83,19 @@ public class CameraMetadata implements Parcelable, AutoCloseable { * type to the key. */ public <T> void set(Key<T> key, T value) { - Log.e(TAG, "Not fully implemented yet"); + int tag = key.getTag(); - mMetadataMap.put(key, value); + int nativeType = getNativeType(tag); + + int size = packSingle(value, null, key.mType, nativeType, /* sizeOnly */true); + + // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. + byte[] values = new byte[size]; + + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + packSingle(value, buffer, key.mType, nativeType, /*sizeOnly*/false); + + writeValues(tag, values); } /** @@ -82,14 +103,536 @@ public class CameraMetadata implements Parcelable, AutoCloseable { * found in {@link CameraProperties}, {@link CaptureResult}, and * {@link CaptureRequest}. * + * @throws IllegalArgumentException if the key was not valid + * * @param key the metadata field to read. * @return the value of that key, or {@code null} if the field is not set. */ @SuppressWarnings("unchecked") public <T> T get(Key<T> key) { - Log.e(TAG, "Not fully implemented yet"); + int tag = key.getTag(); + byte[] values = readValues(tag); + if (values == null) { + return null; + } + + int nativeType = getNativeType(tag); + + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + return unpackSingle(buffer, key.mType, nativeType); + } + + // Keep up-to-date with camera_metadata.h + /** + * @hide + */ + public static final int TYPE_BYTE = 0; + /** + * @hide + */ + public static final int TYPE_INT32 = 1; + /** + * @hide + */ + public static final int TYPE_FLOAT = 2; + /** + * @hide + */ + public static final int TYPE_INT64 = 3; + /** + * @hide + */ + public static final int TYPE_DOUBLE = 4; + /** + * @hide + */ + public static final int TYPE_RATIONAL = 5; + /** + * @hide + */ + public static final int NUM_TYPES = 6; + + private static int getTypeSize(int nativeType) { + switch(nativeType) { + case TYPE_BYTE: + return 1; + case TYPE_INT32: + case TYPE_FLOAT: + return 4; + case TYPE_INT64: + case TYPE_DOUBLE: + case TYPE_RATIONAL: + return 8; + } + + throw new UnsupportedOperationException("Unknown type, can't get size " + + nativeType); + } + + private static Class<?> getExpectedType(int nativeType) { + switch(nativeType) { + case TYPE_BYTE: + return Byte.TYPE; + case TYPE_INT32: + return Integer.TYPE; + case TYPE_FLOAT: + return Float.TYPE; + case TYPE_INT64: + return Long.TYPE; + case TYPE_DOUBLE: + return Double.TYPE; + case TYPE_RATIONAL: + return Rational.class; + } + + throw new UnsupportedOperationException("Unknown type, can't map to Java type " + + nativeType); + } + + @SuppressWarnings("unchecked") + private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type, + int nativeType, boolean sizeOnly) { + + if (!sizeOnly) { + /** + * Rewrite types when the native type doesn't match the managed type + * - Boolean -> Byte + * - Integer -> Byte + */ + + if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { + // Since a boolean can't be cast to byte, and we don't want to use putBoolean + boolean asBool = (Boolean) value; + byte asByte = (byte) (asBool ? 1 : 0); + value = (T) (Byte) asByte; + } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { + int asInt = (Integer) value; + byte asByte = (byte) asInt; + value = (T) (Byte) asByte; + } else if (type != getExpectedType(nativeType)) { + throw new UnsupportedOperationException("Tried to pack a type of " + type + + " but we expected the type to be " + getExpectedType(nativeType)); + } + + if (nativeType == TYPE_BYTE) { + buffer.put((Byte) value); + } else if (nativeType == TYPE_INT32) { + buffer.putInt((Integer) value); + } else if (nativeType == TYPE_FLOAT) { + buffer.putFloat((Float) value); + } else if (nativeType == TYPE_INT64) { + buffer.putLong((Long) value); + } else if (nativeType == TYPE_DOUBLE) { + buffer.putDouble((Double) value); + } else if (nativeType == TYPE_RATIONAL) { + Rational r = (Rational) value; + buffer.putInt(r.getNumerator()); + buffer.putInt(r.getDenominator()); + } + + } + + return getTypeSize(nativeType); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType, + boolean sizeOnly) { + + int size = 0; + + if (type.isPrimitive() || type == Rational.class) { + size = packSingleNative(value, buffer, type, nativeType, sizeOnly); + } else if (type.isEnum()) { + size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly); + } else if (type.isArray()) { + size = packArray(value, buffer, type, nativeType, sizeOnly); + } else { + size = packClass(value, buffer, type, nativeType, sizeOnly); + } + + return size; + } + + private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type, + int nativeType, boolean sizeOnly) { + + // TODO: add support for enums with their own values. + return packSingleNative(value.ordinal(), buffer, Integer.TYPE, nativeType, sizeOnly); + } + + @SuppressWarnings("unchecked") + private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType, + boolean sizeOnly) { + + /** + * FIXME: This doesn't actually work because getFields() returns fields in an unordered + * manner. Although we could sort and get the data to come out correctly on the *java* side, + * it would not be data-compatible with our strict XML definitions. + * + * Rewrite this code to use Parcelables instead, they are most likely compatible with + * what we are trying to do in general. + */ + List<Field> instanceFields = findInstanceFields(type); + if (instanceFields.size() == 0) { + throw new UnsupportedOperationException("Class has no instance fields: " + type); + } + + int fieldCount = instanceFields.size(); + int bufferSize = 0; + + HashSet<Class<?>> fieldTypes = new HashSet<Class<?>>(); + for (Field f : instanceFields) { + fieldTypes.add(f.getType()); + } + + /** + * Pack arguments one field at a time. If we can't access field, look for its accessor + * method instead. + */ + for (int i = 0; i < fieldCount; ++i) { + Object arg; + + Field f = instanceFields.get(i); + + if ((f.getModifiers() & Modifier.PUBLIC) != 0) { + try { + arg = f.get(value); + } catch (IllegalAccessException e) { + throw new UnsupportedOperationException( + "Failed to access field " + f + " of type " + type, e); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException( + "Illegal arguments when accessing field " + f + " of type " + type, e); + } + } else { + Method accessor = null; + // try to find a public accessor method + for(Method m : type.getMethods()) { + Log.v(TAG, String.format("Looking for getter in method %s for field %s", m, f)); + + // Must have 0 arguments + if (m.getParameterTypes().length != 0) { + continue; + } + + // Return type must be same as field type + if (m.getReturnType() != f.getType()) { + continue; + } + + // Strip 'm' from variable prefix if the next letter is capitalized + String fieldName = f.getName(); + char[] nameChars = f.getName().toCharArray(); + if (nameChars.length >= 2 && nameChars[0] == 'm' + && Character.isUpperCase(nameChars[1])) { + fieldName = String.valueOf(nameChars, /*start*/1, nameChars.length - 1); + } + + Log.v(TAG, String.format("Normalized field name: %s", fieldName)); + + // #getFoo() , getfoo(), foo(), all match. + if (m.getName().toLowerCase().equals(fieldName.toLowerCase()) || + m.getName().toLowerCase().equals("get" + fieldName.toLowerCase())) { + accessor = m; + break; + } + } + + if (accessor == null) { + throw new UnsupportedOperationException( + "Failed to find getter method for field " + f + " in type " + type); + } + + try { + arg = accessor.invoke(value); + } catch (IllegalAccessException e) { + // Impossible + throw new UnsupportedOperationException("Failed to access method + " + accessor + + " in type " + type, e); + } catch (IllegalArgumentException e) { + // Impossible + throw new UnsupportedOperationException("Bad arguments for method + " + accessor + + " in type " + type, e); + } catch (InvocationTargetException e) { + // Possibly but extremely unlikely + throw new UnsupportedOperationException("Failed to invoke method + " + accessor + + " in type " + type, e); + } + } + + bufferSize += packSingle(arg, buffer, (Class<Object>)f.getType(), nativeType, sizeOnly); + } + + return bufferSize; + } + + private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType, + boolean sizeOnly) { + + int size = 0; + int arrayLength = Array.getLength(value); + + @SuppressWarnings("unchecked") + Class<Object> componentType = (Class<Object>)type.getComponentType(); + + for (int i = 0; i < arrayLength; ++i) { + size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly); + } + + return size; + } + + @SuppressWarnings("unchecked") + private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) { + + T val; + + if (nativeType == TYPE_BYTE) { + val = (T) (Byte) buffer.get(); + } else if (nativeType == TYPE_INT32) { + val = (T) (Integer) buffer.getInt(); + } else if (nativeType == TYPE_FLOAT) { + val = (T) (Float) buffer.getFloat(); + } else if (nativeType == TYPE_INT64) { + val = (T) (Long) buffer.getLong(); + } else if (nativeType == TYPE_DOUBLE) { + val = (T) (Double) buffer.getDouble(); + } else if (nativeType == TYPE_RATIONAL) { + val = (T) new Rational(buffer.getInt(), buffer.getInt()); + } else { + throw new UnsupportedOperationException("Unknown type, can't unpack a native type " + + nativeType); + } + + /** + * Rewrite types when the native type doesn't match the managed type + * - Byte -> Boolean + * - Byte -> Integer + */ + + if (nativeType == TYPE_BYTE && type == Boolean.TYPE) { + // Since a boolean can't be cast to byte, and we don't want to use getBoolean + byte asByte = (Byte) val; + boolean asBool = asByte != 0; + val = (T) (Boolean) asBool; + } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) { + byte asByte = (Byte) val; + int asInt = asByte; + val = (T) (Integer) asInt; + } else if (type != getExpectedType(nativeType)) { + throw new UnsupportedOperationException("Tried to unpack a type of " + type + + " but we expected the type to be " + getExpectedType(nativeType)); + } + + return val; + } + + private static <T> List<Field> findInstanceFields(Class<T> type) { + List<Field> fields = new ArrayList<Field>(); + + for (Field f : type.getDeclaredFields()) { + if (f.isSynthetic()) { + throw new UnsupportedOperationException( + "Marshalling synthetic fields not supported in type " + type); + } + + // Skip static fields + int modifiers = f.getModifiers(); + if ((modifiers & Modifier.STATIC) == 0) { + fields.add(f); + } + + Log.v(TAG, String.format("Field %s has modifiers %d", f, modifiers)); + } + + if (type.getDeclaredFields().length == 0) { + Log.w(TAG, String.format("Type %s had 0 fields of any kind", type)); + } + return fields; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) { + + if (type.isPrimitive() || type == Rational.class) { + return unpackSingleNative(buffer, type, nativeType); + } + + if (type.isEnum()) { + return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType); + } + + if (type.isArray()) { + return unpackArray(buffer, type, nativeType); + } + + T instance = unpackClass(buffer, type, nativeType); + + return instance; + } + + private static <T> Constructor<T> findApplicableConstructor(Class<T> type) { + + List<Field> instanceFields = findInstanceFields(type); + if (instanceFields.size() == 0) { + throw new UnsupportedOperationException("Class has no instance fields: " + type); + } + + Constructor<T> constructor = null; + int fieldCount = instanceFields.size(); + + HashSet<Class<?>> fieldTypes = new HashSet<Class<?>>(); + for (Field f : instanceFields) { + fieldTypes.add(f.getType()); + } + + /** + * Find which constructor to use: + * - must be public + * - same amount of arguments as there are instance fields + * - each argument is same type as each field (in any order) + */ + @SuppressWarnings("unchecked") + Constructor<T>[] constructors = (Constructor<T>[]) type.getConstructors(); + for (Constructor<T> ctor : constructors) { + Log.v(TAG, String.format("Inspecting constructor '%s'", ctor)); + + Class<?>[] parameterTypes = ctor.getParameterTypes(); + if (parameterTypes.length == fieldCount) { + boolean match = true; + + HashSet<Class<?>> argTypes = new HashSet<Class<?>>(); + for (Class<?> t : parameterTypes) { + argTypes.add(t); + } + + // Order does not matter + match = argTypes.equals(fieldTypes); + + /* + // check if the types are the same + for (int i = 0; i < fieldCount; ++i) { + if (parameterTypes[i] != instanceFields.get(i).getType()) { + + Log.v(TAG, String.format( + "Constructor arg (%d) type %s did not match field type %s", i, + parameterTypes[i], instanceFields.get(i).getType())); + + match = false; + break; + } + } + */ + + if (match) { + constructor = ctor; + break; + } else { + Log.w(TAG, String.format("Constructor args did not have matching types")); + } + } else { + Log.v(TAG, String.format( + "Constructor did not have expected amount of fields (had %d, expected %d)", + parameterTypes.length, fieldCount)); + } + } + + if (constructors.length == 0) { + Log.w(TAG, String.format("Type %s had no public constructors", type)); + } + + if (constructor == null) { + throw new UnsupportedOperationException( + "Failed to find any applicable constructors for type " + type); + } + + return constructor; + } + + private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type, + int nativeType) { - return (T) mMetadataMap.get(key); + // TODO: add support for enums with their own values. + + T[] values = type.getEnumConstants(); + int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType); + + if (ordinal < 0 || ordinal >= values.length) { + Log.e(TAG, String.format("Got invalid enum value %d for type %s, assuming it's 0", + ordinal, type)); + ordinal = 0; + } + + return values[ordinal]; + } + + private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) { + + /** + * FIXME: This doesn't actually work because getFields() returns fields in an unordered + * manner. Although we could sort and get the data to come out correctly on the *java* side, + * it would not be data-compatible with our strict XML definitions. + * + * Rewrite this code to use Parcelables instead, they are most likely compatible with + * what we are trying to do in general. + */ + + List<Field> instanceFields = findInstanceFields(type); + if (instanceFields.size() == 0) { + throw new UnsupportedOperationException("Class has no instance fields: " + type); + } + int fieldCount = instanceFields.size(); + + Constructor<T> constructor = findApplicableConstructor(type); + + /** + * Build the arguments by unpacking one field at a time + * (note that while the field type might be different, the native type is the same) + */ + Object[] arguments = new Object[fieldCount]; + for (int i = 0; i < fieldCount; ++i) { + Object o = unpackSingle(buffer, instanceFields.get(i).getType(), nativeType); + arguments[i] = o; + } + + T instance; + try { + instance = constructor.newInstance(arguments); + } catch (InstantiationException e) { + // type is abstract class, interface, etc... + throw new UnsupportedOperationException("Failed to instantiate type " + type, e); + } catch (IllegalAccessException e) { + // This could happen if we have to access a private. + throw new UnsupportedOperationException("Failed to access type " + type, e); + } catch (IllegalArgumentException e) { + throw new UnsupportedOperationException("Illegal arguments for constructor of type " + + type, e); + } catch (InvocationTargetException e) { + throw new UnsupportedOperationException( + "Underlying constructor threw exception for type " + type, e); + } + + return instance; + } + + @SuppressWarnings("unchecked") + private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) { + + Class<?> componentType = type.getComponentType(); + Object array; + + int remaining = buffer.remaining(); + // FIXME: Assumes that the rest of the ByteBuffer is part of the array. + int arraySize = remaining / getTypeSize(nativeType); + + array = Array.newInstance(componentType, arraySize); + for (int i = 0; i < arraySize; ++i) { + Object elem = unpackSingle(buffer, componentType, nativeType); + Array.set(array, i, elem); + } + + return (T) array; } @Override @@ -111,11 +654,22 @@ public class CameraMetadata implements Parcelable, AutoCloseable { } public static class Key<T> { - public Key(String name) { + + private boolean mHasTag; + private int mTag; + private final Class<T> mType; + + /* + * @hide + */ + public Key(String name, Class<T> type) { if (name == null) { throw new NullPointerException("Key needs a valid name"); + } else if (type == null) { + throw new NullPointerException("Type needs to be non-null"); } mName = name; + mType = type; } public final String getName() { @@ -144,13 +698,30 @@ public class CameraMetadata implements Parcelable, AutoCloseable { } private final String mName; + + /** + * <p> + * Get the tag corresponding to this key. This enables insertion into the + * native metadata. + * </p> + * + * <p>This value is looked up the first time, and cached subsequently.</p> + * + * @return the tag numeric value corresponding to the string + * + * @hide + */ + public final int getTag() { + if (!mHasTag) { + mTag = CameraMetadata.getTag(mName); + mHasTag = true; + } + return mTag; + } } private final Map<Key<?>, Object> mMetadataMap; - /** - * @hide - */ private long mMetadataPtr; // native CameraMetadata* private native long nativeAllocate(); @@ -160,6 +731,14 @@ public class CameraMetadata implements Parcelable, AutoCloseable { private native synchronized void nativeClose(); private native synchronized boolean nativeIsEmpty(); private native synchronized int nativeGetEntryCount(); + + private native synchronized byte[] nativeReadValues(int tag); + private native synchronized void nativeWriteValues(int tag, byte[] src); + + private static native int nativeGetTagFromKey(String keyName) + throws IllegalArgumentException; + private static native int nativeGetTypeFromTag(int tag) + throws IllegalArgumentException; private static native void nativeClassInit(); /** @@ -214,6 +793,61 @@ public class CameraMetadata implements Parcelable, AutoCloseable { } } + /** + * Convert a key string into the equivalent native tag. + * + * @throws IllegalArgumentException if the key was not recognized + * @throws NullPointerException if the key was null + * + * @hide + */ + public static int getTag(String key) { + return nativeGetTagFromKey(key); + } + + /** + * Get the underlying native type for a tag. + * + * @param tag an integer tag, see e.g. {@link #getTag} + * @return an int enum for the metadata type, see e.g. {@link #TYPE_BYTE} + * + * @hide + */ + public static int getNativeType(int tag) { + return nativeGetTypeFromTag(tag); + } + + /** + * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing + * the entry if src was null.</p> + * + * <p>An empty array can be passed in to update the entry to 0 elements.</p> + * + * @param tag an integer tag, see e.g. {@link #getTag} + * @param src an array of bytes, or null to erase the entry + * + * @hide + */ + public void writeValues(int tag, byte[] src) { + nativeWriteValues(tag, src); + } + + /** + * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize + * the data properly.</p> + * + * <p>An empty array can be returned to denote an existing entry with 0 elements.</p> + * + * @param tag an integer tag, see e.g. {@link #getTag} + * + * @return null if there were 0 entries for this tag, a byte[] otherwise. + * @hide + */ + public byte[] readValues(int tag) { + // TODO: Optimization. Native code returns a ByteBuffer instead. + return nativeReadValues(tag); + } + @Override protected void finalize() throws Throwable { try { @@ -230,4 +864,4 @@ public class CameraMetadata implements Parcelable, AutoCloseable { System.loadLibrary("media_jni"); nativeClassInit(); } -}
\ No newline at end of file +} diff --git a/core/java/android/hardware/photography/CameraProperties.java b/core/java/android/hardware/photography/CameraProperties.java index ad42285..2ed4e3a 100644 --- a/core/java/android/hardware/photography/CameraProperties.java +++ b/core/java/android/hardware/photography/CameraProperties.java @@ -40,7 +40,7 @@ public final class CameraProperties extends CameraMetadata { * removable cameras of the same model. */ public static final Key<String> INFO_MODEL = - new Key<String>("android.info.model"); + new Key<String>("android.info.model", String.class); /** * A unique identifier for this camera. For removable cameras, every @@ -49,7 +49,7 @@ public final class CameraProperties extends CameraMetadata { * identifier is equal to the the device's id. */ public static final Key<String> INFO_IDENTIFIER = - new Key<String>("android.info.identifier"); + new Key<String>("android.info.identifier", String.class); /** * <p>Whether this camera is removable or not.</p> @@ -59,7 +59,7 @@ public final class CameraProperties extends CameraMetadata { * determine if this camera is a match for a camera device seen earlier.</p> */ public static final Key<Boolean> INFO_REMOVABLE = - new Key<Boolean>("android.info.isRemovable"); + new Key<Boolean>("android.info.isRemovable", Boolean.TYPE); /** * <p>The hardware operational model of this device. One of the @@ -100,7 +100,7 @@ public final class CameraProperties extends CameraMetadata { * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL */ public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL = - new Key<Integer>("android.info.supportedHardwareLevel"); + new Key<Integer>("android.info.supportedHardwareLevel", Integer.TYPE); /** * <p>The type reported by limited-capability camera devices.</p> @@ -164,8 +164,8 @@ public final class CameraProperties extends CameraMetadata { * {@link android.graphics.ImageFormat#YUV_420_888} are guaranteed to be * supported.</p> */ - public static final Key<Integer[]> SCALER_AVAILABLE_FORMATS = - new Key<Integer[]>("android.scaler.availableFormats"); + public static final Key<int[]> SCALER_AVAILABLE_FORMATS = + new Key<int[]>("android.scaler.availableFormats", int[].class); /** * <p>The available output sizes for JPEG buffers from this camera @@ -174,7 +174,7 @@ public final class CameraProperties extends CameraMetadata { * when using format {@link android.graphics.ImageFormat#JPEG}.</p> */ public static final Key<Size[]> SCALER_AVAILABLE_JPEG_SIZES = - new Key<Size[]>("android.scaler.availableJpegSizes"); + new Key<Size[]>("android.scaler.availableJpegSizes", Size[].class); /** * <p>The available sizes for output buffers from this camera device, when @@ -194,7 +194,7 @@ public final class CameraProperties extends CameraMetadata { * */ public static final Key<Size[]> SCALER_AVAILABLE_PROCESSED_SIZES = - new Key<Size[]>("android.scaler.availableProcessedSizes"); + new Key<Size[]>("android.scaler.availableProcessedSizes", Size[].class); /** * <p>The available sizes for output buffers from this camera device, when @@ -207,7 +207,7 @@ public final class CameraProperties extends CameraMetadata { * when using image format {@link android.graphics.ImageFormat#RAW_SENSOR}.</p> */ public static final Key<Size[]> SCALER_AVAILABLE_RAW_SIZES = - new Key<Size[]>("android.scaler.availableRawSizes"); + new Key<Size[]>("android.scaler.availableRawSizes", Size[].class); /** * <p>The coordinates of the sensor's active pixel array, relative to its @@ -230,7 +230,7 @@ public final class CameraProperties extends CameraMetadata { * being the top-left corner of the active array.</p> */ public static final Key<Rect> SENSOR_ACTIVE_ARRAY_SIZE = - new Key<Rect>("android.sensor.activeArraySize"); + new Key<Rect>("android.sensor.activeArraySize", Rect.class); /** * <p>The size of the sensor's total pixel array available for readout. Some @@ -240,7 +240,7 @@ public final class CameraProperties extends CameraMetadata { * the supported capture sizes.</p> */ public static final Key<Size> SENSOR_PIXEL_ARRAY_SIZE = - new Key<Size>("android.sensor.activeArraySize"); + new Key<Size>("android.sensor.activeArraySize", Size.class); // TODO: Many more of these. diff --git a/core/java/android/hardware/photography/CaptureRequest.java b/core/java/android/hardware/photography/CaptureRequest.java index ac2041b..d4a7a3c 100644 --- a/core/java/android/hardware/photography/CaptureRequest.java +++ b/core/java/android/hardware/photography/CaptureRequest.java @@ -58,14 +58,14 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * The exposure time for this capture, in nanoseconds. */ public static final Key<Long> SENSOR_EXPOSURE_TIME = - new Key<Long>("android.sensor.exposureTime"); + new Key<Long>("android.sensor.exposureTime", Long.TYPE); /** * The sensor sensitivity (gain) setting for this camera. * This is represented as an ISO sensitivity value */ public static final Key<Integer> SENSOR_SENSITIVITY = - new Key<Integer>("android.sensor.sensitivity"); + new Key<Integer>("android.sensor.sensitivity", Integer.TYPE); // Many more settings diff --git a/core/java/android/hardware/photography/CaptureResult.java b/core/java/android/hardware/photography/CaptureResult.java index b502c4c..2fdd466 100644 --- a/core/java/android/hardware/photography/CaptureResult.java +++ b/core/java/android/hardware/photography/CaptureResult.java @@ -42,15 +42,15 @@ public final class CaptureResult extends CameraMetadata { * or {@link android.media.Image#getTimestamp Image.getTimestamp()} for this * capture's image data. */ - public static final Key SENSOR_TIMESTAMP = - new Key<Long>("android.sensor.timestamp"); + public static final Key<Long> SENSOR_TIMESTAMP = + new Key<Long>("android.sensor.timestamp", Long.TYPE); /** * The state of the camera device's auto-exposure algorithm. One of the * CONTROL_AE_STATE_* enumerations. */ - public static final Key CONTROL_AE_STATE = - new Key<Integer>("android.control.aeState"); + public static final Key<Integer> CONTROL_AE_STATE = + new Key<Integer>("android.control.aeState", Integer.TYPE); /** * The auto-exposure algorithm is inactive. @@ -96,8 +96,8 @@ public final class CaptureResult extends CameraMetadata { * The list of faces detected in this capture. Available if face detection * was enabled for this capture */ - public static final Key STATISTICS_DETECTED_FACES = - new Key<Face[]>("android.statistics.faces"); + public static final Key<Face[]> STATISTICS_DETECTED_FACES = + new Key<Face[]>("android.statistics.faces", Face[].class); // TODO: Many many more diff --git a/core/java/android/hardware/photography/Rational.java b/core/java/android/hardware/photography/Rational.java new file mode 100644 index 0000000..66e533e --- /dev/null +++ b/core/java/android/hardware/photography/Rational.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.photography; + +/** + * The rational data type used by CameraMetadata keys. Contains a pair of ints representing the + * numerator and denominator of a Rational number. This type is immutable. + */ +public final class Rational { + private final int mNumerator; + private final int mDenominator; + + /** + * <p>Create a Rational with a given numerator and denominator.</p> + * + * <p> + * The signs of the numerator and the denominator may be flipped such that the denominator + * is always 0. + * </p> + * + * @param numerator the numerator of the rational + * @param denominator the denominator of the rational + * + * @throws IllegalArgumentException if the denominator is 0 + */ + public Rational(int numerator, int denominator) { + + if (denominator == 0) { + throw new IllegalArgumentException("Argument 'denominator' is 0"); + } + + if (denominator < 0) { + numerator = -numerator; + denominator = -denominator; + } + + mNumerator = numerator; + mDenominator = denominator; + } + + /** + * Gets the numerator of the rational. + */ + public int getNumerator() { + return mNumerator; + } + + /** + * Gets the denominator of the rational + */ + public int getDenominator() { + return mDenominator; + } + + /** + * <p>Compare this Rational to another object and see if they are equal.</p> + * + * <p>A Rational object can only be equal to another Rational object (comparing against any other + * type will return false).</p> + * + * <p>A Rational object is considered equal to another Rational object if and only if their + * reduced forms have the same numerator and denominator.</p> + * + * <p>A reduced form of a Rational is calculated by dividing both the numerator and the + * denominator by their greatest common divisor.</p> + * + * <pre> + * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true + * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false + * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction + * </pre> + * + * @param obj a reference to another object + * + * @return boolean that determines whether or not the two Rational objects are equal. + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof Rational) { + Rational other = (Rational) obj; + if(mNumerator == other.mNumerator && mDenominator == other.mDenominator) { + return true; + } else { + int thisGcd = gcd(); + int otherGcd = other.gcd(); + + int thisNumerator = mNumerator / thisGcd; + int thisDenominator = mDenominator / thisGcd; + + int otherNumerator = other.mNumerator / otherGcd; + int otherDenominator = other.mDenominator / otherGcd; + + return (thisNumerator == otherNumerator && thisDenominator == otherDenominator); + } + } + return false; + } + + @Override + public String toString() { + return mNumerator + "/" + mDenominator; + } + + @Override + public int hashCode() { + final long INT_MASK = 0xffffffffL; + + long asLong = INT_MASK & mNumerator; + asLong <<= 32; + + asLong |= (INT_MASK & mDenominator); + + return ((Long)asLong).hashCode(); + } + + /** + * Calculates the greatest common divisor using Euclid's algorithm. + * + * @return int value representing the gcd. Always positive. + * @hide + */ + public int gcd() { + /** + * Non-recursive implementation of Euclid's algorithm: + * + * gcd(a, 0) := a + * gcd(a, b) := gcd(b, a mod b) + * + */ + + int a = mNumerator; + int b = mDenominator; + + while (b != 0) { + int oldB = b; + + b = a % b; + a = oldB; + } + + return Math.abs(a); + } +} diff --git a/core/java/android/hardware/photography/Size.java b/core/java/android/hardware/photography/Size.java index e1115d3..ee7980e 100644 --- a/core/java/android/hardware/photography/Size.java +++ b/core/java/android/hardware/photography/Size.java @@ -24,12 +24,12 @@ public final class Size { /** * Create a new immutable Size instance * - * @param w The width to store in the Size instance - * @param h The height to store in the Size instance + * @param width The width to store in the Size instance + * @param height The height to store in the Size instance */ - Size(int w, int h) { - mWidth = w; - mHeight = h; + Size(int width, int height) { + mWidth = width; + mHeight = height; } public final int getWidth() { diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index 382b25e..62d8738 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -639,6 +639,7 @@ public class NetworkStatsHistory implements Parcelable { @Deprecated public static long[] readFullLongArray(DataInputStream in) throws IOException { final int size = in.readInt(); + if (size < 0) throw new ProtocolException("negative array size"); final long[] values = new long[size]; for (int i = 0; i < values.length; i++) { values[i] = in.readLong(); @@ -680,6 +681,7 @@ public class NetworkStatsHistory implements Parcelable { public static long[] readVarLongArray(DataInputStream in) throws IOException { final int size = in.readInt(); if (size == -1) return null; + if (size < 0) throw new ProtocolException("negative array size"); final long[] values = new long[size]; for (int i = 0; i < values.length; i++) { values[i] = readVarLong(in); diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java index 2314057..9e9521a 100644 --- a/core/java/android/os/StatFs.java +++ b/core/java/android/os/StatFs.java @@ -18,14 +18,14 @@ package android.os; import libcore.io.ErrnoException; import libcore.io.Libcore; -import libcore.io.StructStatFs; +import libcore.io.StructStatVfs; /** * Retrieve overall information about the space on a filesystem. This is a - * wrapper for Unix statfs(). + * wrapper for Unix statvfs(). */ public class StatFs { - private StructStatFs mStat; + private StructStatVfs mStat; /** * Construct a new StatFs for looking at the stats of the filesystem at @@ -39,9 +39,9 @@ public class StatFs { mStat = doStat(path); } - private static StructStatFs doStat(String path) { + private static StructStatVfs doStat(String path) { try { - return Libcore.os.statfs(path); + return Libcore.os.statvfs(path); } catch (ErrnoException e) { throw new IllegalArgumentException("Invalid path: " + path, e); } @@ -66,7 +66,7 @@ public class StatFs { /** * The size, in bytes, of a block on the file system. This corresponds to - * the Unix {@code statfs.f_bsize} field. + * the Unix {@code statvfs.f_bsize} field. */ public long getBlockSizeLong() { return mStat.f_bsize; @@ -82,7 +82,7 @@ public class StatFs { /** * The total number of blocks on the file system. This corresponds to the - * Unix {@code statfs.f_blocks} field. + * Unix {@code statvfs.f_blocks} field. */ public long getBlockCountLong() { return mStat.f_blocks; @@ -99,7 +99,7 @@ public class StatFs { /** * The total number of blocks that are free on the file system, including * reserved blocks (that are not available to normal applications). This - * corresponds to the Unix {@code statfs.f_bfree} field. Most applications + * corresponds to the Unix {@code statvfs.f_bfree} field. Most applications * will want to use {@link #getAvailableBlocks()} instead. */ public long getFreeBlocksLong() { @@ -125,7 +125,7 @@ public class StatFs { /** * The number of blocks that are free on the file system and available to - * applications. This corresponds to the Unix {@code statfs.f_bavail} field. + * applications. This corresponds to the Unix {@code statvfs.f_bavail} field. */ public long getAvailableBlocksLong() { return mStat.f_bavail; diff --git a/core/java/android/view/GraphicBuffer.java b/core/java/android/view/GraphicBuffer.java index b4576f3..30c077c 100644 --- a/core/java/android/view/GraphicBuffer.java +++ b/core/java/android/view/GraphicBuffer.java @@ -62,6 +62,9 @@ public class GraphicBuffer implements Parcelable { private Canvas mCanvas; private int mSaveCount; + // If set to true, this GraphicBuffer instance cannot be used anymore + private boolean mDestroyed; + /** * Creates new <code>GraphicBuffer</code> instance. This method will return null * if the buffer cannot be created. @@ -128,10 +131,14 @@ public class GraphicBuffer implements Parcelable { * <p>The content of the buffer is preserved between unlockCanvas() * and lockCanvas().</p> * + * <p>If this method is called after {@link #destroy()}, the return value will + * always be null.</p> + * * @return A Canvas used to draw into the buffer, or null. * * @see #lockCanvas(android.graphics.Rect) * @see #unlockCanvasAndPost(android.graphics.Canvas) + * @see #isDestroyed() */ public Canvas lockCanvas() { return lockCanvas(null); @@ -141,14 +148,22 @@ public class GraphicBuffer implements Parcelable { * Just like {@link #lockCanvas()} but allows specification of a dirty * rectangle. * + * <p>If this method is called after {@link #destroy()}, the return value will + * always be null.</p> + * * @param dirty Area of the buffer that may be modified. - * @return A Canvas used to draw into the surface or null + * @return A Canvas used to draw into the surface, or null. * * @see #lockCanvas() * @see #unlockCanvasAndPost(android.graphics.Canvas) + * @see #isDestroyed() */ public Canvas lockCanvas(Rect dirty) { + if (mDestroyed) { + return null; + } + if (mCanvas == null) { mCanvas = new Canvas(); } @@ -164,13 +179,17 @@ public class GraphicBuffer implements Parcelable { /** * Finish editing pixels in the buffer. * + * <p>This method doesn't do anything if {@link #destroy()} was + * previously called.</p> + * * @param canvas The Canvas previously returned by lockCanvas() * * @see #lockCanvas() * @see #lockCanvas(android.graphics.Rect) + * @see #isDestroyed() */ public void unlockCanvasAndPost(Canvas canvas) { - if (mCanvas != null && canvas == mCanvas) { + if (!mDestroyed && mCanvas != null && canvas == mCanvas) { canvas.restoreToCount(mSaveCount); mSaveCount = 0; @@ -178,10 +197,39 @@ public class GraphicBuffer implements Parcelable { } } + /** + * Destroyes this buffer immediately. Calling this method frees up any + * underlying native resources. After calling this method, this buffer + * must not be used in any way ({@link #lockCanvas()} must not be called, + * etc.) + * + * @see #isDestroyed() + */ + public void destroy() { + if (!mDestroyed) { + mDestroyed = true; + nDestroyGraphicBuffer(mNativeObject); + } + } + + /** + * Indicates whether this buffer has been destroyed. A destroyed buffer + * cannot be used in any way: locking a Canvas will return null, the buffer + * cannot be written to a parcel, etc. + * + * @return True if this <code>GraphicBuffer</code> is in a destroyed state, + * false otherwise. + * + * @see #destroy() + */ + public boolean isDestroyed() { + return mDestroyed; + } + @Override protected void finalize() throws Throwable { try { - nDestroyGraphicBuffer(mNativeObject); + if (!mDestroyed) nDestroyGraphicBuffer(mNativeObject); } finally { super.finalize(); } @@ -192,8 +240,23 @@ public class GraphicBuffer implements Parcelable { return 0; } + /** + * Flatten this object in to a Parcel. + * + * <p>Calling this method will throw an <code>IllegalStateException</code> if + * {@link #destroy()} has been previously called.</p> + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + */ @Override public void writeToParcel(Parcel dest, int flags) { + if (mDestroyed) { + throw new IllegalStateException("This GraphicBuffer has been destroyed and cannot be " + + "written to a parcel."); + } + dest.writeInt(mWidth); dest.writeInt(mHeight); dest.writeInt(mFormat); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 281bd7e..d9e4600 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1983,6 +1983,7 @@ public abstract class HardwareRenderer { if (map != null) { GLES20Canvas.initAtlas(buffer, map); } + buffer.destroy(); } } } catch (RemoteException e) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1192390..fddc26c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7051,7 +7051,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) { mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED; if (mParent != null) { - mParent.childAccessibilityStateChanged(this); + try { + mParent.childAccessibilityStateChanged(this); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } } } } @@ -11944,10 +11949,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (!canResolveLayoutDirection()) return false; // Parent has not yet resolved, LTR is still the default - if (!mParent.isLayoutDirectionResolved()) return false; + try { + if (!mParent.isLayoutDirectionResolved()) return false; - if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { - mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL; + if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { + mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL; + } + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); } break; case LAYOUT_DIRECTION_RTL: @@ -11973,13 +11983,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Check if layout direction resolution can be done. * * @return true if layout direction resolution can be done otherwise return false. - * - * @hide */ public boolean canResolveLayoutDirection() { switch (getRawLayoutDirection()) { case LAYOUT_DIRECTION_INHERIT: - return (mParent != null) && mParent.canResolveLayoutDirection(); + if (mParent != null) { + try { + return mParent.canResolveLayoutDirection(); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } + } + return false; + default: return true; } @@ -12007,7 +12024,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return true if layout direction has been resolved. - * @hide */ public boolean isLayoutDirectionResolved() { return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) == PFLAG2_LAYOUT_DIRECTION_RESOLVED; @@ -17181,14 +17197,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // Parent has not yet resolved, so we still return the default - if (!mParent.isTextDirectionResolved()) { - mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; - // Resolution will need to happen again later - return false; + try { + if (!mParent.isTextDirectionResolved()) { + mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; + // Resolution will need to happen again later + return false; + } + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); } // Set current resolved direction to the same value as the parent's one - final int parentResolvedDirection = mParent.getTextDirection(); + int parentResolvedDirection; + try { + parentResolvedDirection = mParent.getTextDirection(); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + parentResolvedDirection = TEXT_DIRECTION_LTR; + } switch (parentResolvedDirection) { case TEXT_DIRECTION_FIRST_STRONG: case TEXT_DIRECTION_ANY_RTL: @@ -17229,13 +17257,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Check if text direction resolution can be done. * * @return true if text direction resolution can be done otherwise return false. - * - * @hide */ public boolean canResolveTextDirection() { switch (getRawTextDirection()) { case TEXT_DIRECTION_INHERIT: - return (mParent != null) && mParent.canResolveTextDirection(); + if (mParent != null) { + try { + return mParent.canResolveTextDirection(); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } + } + return false; + default: return true; } @@ -17265,8 +17300,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return true if text direction is resolved. - * - * @hide */ public boolean isTextDirectionResolved() { return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED) == PFLAG2_TEXT_DIRECTION_RESOLVED; @@ -17393,13 +17426,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // Parent has not yet resolved, so we still return the default - if (!mParent.isTextAlignmentResolved()) { - mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; - // Resolution will need to happen again later - return false; + try { + if (!mParent.isTextAlignmentResolved()) { + mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; + // Resolution will need to happen again later + return false; + } + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); } - final int parentResolvedTextAlignment = mParent.getTextAlignment(); + int parentResolvedTextAlignment; + try { + parentResolvedTextAlignment = mParent.getTextAlignment(); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + parentResolvedTextAlignment = TEXT_ALIGNMENT_GRAVITY; + } switch (parentResolvedTextAlignment) { case TEXT_ALIGNMENT_GRAVITY: case TEXT_ALIGNMENT_TEXT_START: @@ -17444,13 +17489,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Check if text alignment resolution can be done. * * @return true if text alignment resolution can be done otherwise return false. - * - * @hide */ public boolean canResolveTextAlignment() { switch (getRawTextAlignment()) { case TEXT_DIRECTION_INHERIT: - return (mParent != null) && mParent.canResolveTextAlignment(); + if (mParent != null) { + try { + return mParent.canResolveTextAlignment(); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } + } + return false; + default: return true; } @@ -17480,8 +17532,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return true if text alignment is resolved. - * - * @hide */ public boolean isTextAlignmentResolved() { return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED) == PFLAG2_TEXT_ALIGNMENT_RESOLVED; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index a40582b..f574efa 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -742,8 +742,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Called when a child view has changed whether or not it is tracking transient state. - * - * @hide */ public void childHasTransientStateChanged(View child, boolean childHasTransientState) { final boolean oldHasTransientState = hasTransientState(); @@ -764,9 +762,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - /** - * @hide - */ @Override public boolean hasTransientState() { return mChildCountWithTransientState > 0 || super.hasTransientState(); @@ -2530,13 +2525,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager event.setClassName(ViewGroup.class.getName()); } - /** - * @hide - */ @Override public void childAccessibilityStateChanged(View root) { if (mParent != null) { - mParent.childAccessibilityStateChanged(root); + try { + mParent.childAccessibilityStateChanged(root); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } } } diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 2ebc1a1..8ae6996 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -271,8 +271,6 @@ public interface ViewParent { * * @param child Child view whose state has changed * @param hasTransientState true if this child has transient state - * - * @hide */ public void childHasTransientStateChanged(View child, boolean hasTransientState); @@ -295,9 +293,7 @@ public interface ViewParent { * A child notifies its parent that the accessibility state of a subtree rooted * at a given node changed. That is the structure of the subtree is different. * - * @param The root of the changed subtree. - * - * @hide + * @param root The root of the changed subtree. */ public void childAccessibilityStateChanged(View root); @@ -306,8 +302,6 @@ public interface ViewParent { * See {@link View#setLayoutDirection(int)} * * @return True if this view parent can resolve the layout direction. - * - * @hide */ public boolean canResolveLayoutDirection(); @@ -316,8 +310,6 @@ public interface ViewParent { * See {@link View#setLayoutDirection(int)} * * @return True if this view parent layout direction is resolved. - * - * @hide */ public boolean isLayoutDirectionResolved(); @@ -326,8 +318,6 @@ public interface ViewParent { * * @return {@link View#LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns * {@link View#LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. - * - * @hide */ public int getLayoutDirection(); @@ -336,8 +326,6 @@ public interface ViewParent { * See {@link View#setTextDirection(int)} * * @return True if this view parent can resolve the text direction. - * - * @hide */ public boolean canResolveTextDirection(); @@ -346,8 +334,6 @@ public interface ViewParent { * See {@link View#setTextDirection(int)} * * @return True if this view parent text direction is resolved. - * - * @hide */ public boolean isTextDirectionResolved(); @@ -361,8 +347,6 @@ public interface ViewParent { * {@link View#TEXT_DIRECTION_LTR}, * {@link View#TEXT_DIRECTION_RTL}, * {@link View#TEXT_DIRECTION_LOCALE} - * - * @hide */ public int getTextDirection(); @@ -371,8 +355,6 @@ public interface ViewParent { * See {@link View#setTextAlignment(int)} * * @return True if this view parent can resolve the text alignment. - * - * @hide */ public boolean canResolveTextAlignment(); @@ -381,8 +363,6 @@ public interface ViewParent { * See {@link View#setTextAlignment(int)} * * @return True if this view parent text alignment is resolved. - * - * @hide */ public boolean isTextAlignmentResolved(); @@ -397,8 +377,6 @@ public interface ViewParent { * {@link View#TEXT_ALIGNMENT_TEXT_END}, * {@link View#TEXT_ALIGNMENT_VIEW_START}, * {@link View#TEXT_ALIGNMENT_VIEW_END} - * - * @hide */ public int getTextAlignment(); } diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java index 3c5b6fa..c2aa90b 100644 --- a/core/java/android/view/transition/Fade.java +++ b/core/java/android/view/transition/Fade.java @@ -96,12 +96,19 @@ public class Fade extends Visibility { protected Animator appear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility) { - View endView = (endValues != null) ? endValues.view : null; - if ((mFadingMode & IN) != IN) { + if ((mFadingMode & IN) != IN || endValues == null) { return null; } + final View endView = endValues.view; endView.setAlpha(0); - return runAnimation(endView, 0, 1, null); + final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Always end animation with full alpha, in case it's canceled mid-stream + endView.setAlpha(1); + } + }; + return runAnimation(endView, 0, 1, endListener); } @Override diff --git a/core/java/android/view/transition/TransitionGroup.java b/core/java/android/view/transition/TransitionGroup.java index d0e61ea..313e33e 100644 --- a/core/java/android/view/transition/TransitionGroup.java +++ b/core/java/android/view/transition/TransitionGroup.java @@ -104,7 +104,7 @@ public class TransitionGroup extends Transition { mTransitions.add(transitions[i]); transitions[i].mParent = this; if (mDuration >= 0) { - transitions[0].setDuration(mDuration); + transitions[i].setDuration(mDuration); } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4998742..0149f03 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -41,6 +41,7 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.AbsoluteLayout; @@ -2051,6 +2052,13 @@ public class WebView extends AbsoluteLayout } */ + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + AccessibilityNodeProvider provider = + mProvider.getViewDelegate().getAccessibilityNodeProvider(); + return provider == null ? super.getAccessibilityNodeProvider() : provider; + } + @Deprecated @Override public boolean shouldDelayChildPressedState() { diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 3afab09..5a9e6c9 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -90,6 +90,7 @@ import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -1769,6 +1770,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + return null; + } + + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { if (!mWebView.isEnabled()) { // Only default actions are supported while disabled. diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index fa17ab9..41d6333 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -32,6 +32,7 @@ import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.webkit.WebView.HitTestResult; @@ -279,6 +280,8 @@ public interface WebViewProvider { interface ViewDelegate { public boolean shouldDelayChildPressedState(); + public AccessibilityNodeProvider getAccessibilityNodeProvider(); + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info); public void onInitializeAccessibilityEvent(AccessibilityEvent event); diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 1e20ab2..8b51bf3 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -28,6 +28,7 @@ import android.os.Debug; import android.os.Parcel; import android.os.Parcelable; import android.os.StrictMode; +import android.os.Trace; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; @@ -2151,6 +2152,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * @return A view displaying the data associated with the specified position */ View obtainView(int position, boolean[] isScrap) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView"); + isScrap[0] = false; View scrapView; @@ -2213,6 +2216,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + return child; } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index c4406ac..05a8dc8 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -199,10 +199,8 @@ public abstract class CompoundButton extends Button implements Checkable { unscheduleDrawable(mButtonDrawable); } d.setCallback(this); - d.setState(getDrawableState()); d.setVisible(getVisibility() == VISIBLE, false); mButtonDrawable = d; - mButtonDrawable.setState(null); setMinHeight(mButtonDrawable.getIntrinsicHeight()); } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 63147dd..a7d546a 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Rect; +import android.os.Trace; import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; @@ -1364,6 +1365,8 @@ public class GridView extends AbsListView { */ private void setupChild(View child, int position, int y, boolean flow, int childrenLeft, boolean selected, boolean recycled, int where) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "setupGridItem"); + boolean isSelected = selected && shouldShowSelector(); final boolean updateChildSelected = isSelected != child.isSelected(); final int mode = mTouchMode; @@ -1459,6 +1462,8 @@ public class GridView extends AbsListView { != position) { child.jumpDrawablesToCurrentState(); } + + Trace.traceEnd(Trace.TRACE_TAG_VIEW); } /** diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index d990a20..c44ac32 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -16,6 +16,7 @@ package android.widget; +import android.os.Trace; import com.android.internal.R; import com.android.internal.util.Predicate; import com.google.android.collect.Lists; @@ -1876,6 +1877,8 @@ public class ListView extends AbsListView { */ private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft, boolean selected, boolean recycled) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "setupListItem"); + final boolean isSelected = selected && shouldShowSelector(); final boolean updateChildSelected = isSelected != child.isSelected(); final int mode = mTouchMode; @@ -1956,6 +1959,8 @@ public class ListView extends AbsListView { != position) { child.jumpDrawablesToCurrentState(); } + + Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @Override diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java index fa53cfa..9bec10e 100644 --- a/core/java/com/android/internal/util/FastPrintWriter.java +++ b/core/java/com/android/internal/util/FastPrintWriter.java @@ -458,7 +458,7 @@ public class FastPrintWriter extends PrintWriter { if (lnum == 0) { println("0"); } else { - super.print(lnum); + super.println(lnum); } } diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 464ae2f..35b76dd 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -242,17 +242,20 @@ public class ListMenuItemView extends RelativeLayout implements MenuView.ItemVie private void insertIconView() { mIconView = (ImageView) getInflater() - .inflate(com.android.internal.R.layout.list_menu_item_icon, this, true); + .inflate(com.android.internal.R.layout.list_menu_item_icon, this, false); + addView(mIconView); } private void insertRadioButton() { mRadioButton = (RadioButton) getInflater() - .inflate(com.android.internal.R.layout.list_menu_item_radio, this, true); + .inflate(com.android.internal.R.layout.list_menu_item_radio, this, false); + addView(mRadioButton); } private void insertCheckBox() { mCheckBox = (CheckBox) getInflater() - .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, true); + .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, false); + addView(mCheckBox); } private void alignTextToStartOf(View v) { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 20e5011..d60fa18 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -196,6 +196,7 @@ LOCAL_SHARED_LIBRARIES := \ libgui \ libinput \ libcamera_client \ + libcamera_metadata \ libskia \ libsqlite \ libEGL \ diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 016c11e..3d0c75f 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -116,30 +116,6 @@ static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { } } -static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, - jobject padding) { - - jbyte* array = env->GetByteArrayElements(chunkObject, 0); - if (array != NULL) { - size_t chunkSize = env->GetArrayLength(chunkObject); - void* storage = alloca(chunkSize); - android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); - memcpy(chunk, array, chunkSize); - android::Res_png_9patch::deserialize(chunk); - - scaleNinePatchChunk(chunk, scale); - memcpy(array, chunk, chunkSize); - - if (padding) { - GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, - chunk->paddingRight, chunk->paddingBottom); - } - - env->ReleaseByteArrayElements(chunkObject, array, 0); - } - return chunkObject; -} - static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, int sampleSize, bool ditherImage) { @@ -624,11 +600,6 @@ static JNINativeMethod gMethods[] = { (void*)nativeDecodeByteArray }, - { "nativeScaleNinePatch", - "([BFLandroid/graphics/Rect;)[B", - (void*)nativeScaleNinePatch - }, - { "nativeIsSeekable", "(Ljava/io/FileDescriptor;)Z", (void*)nativeIsSeekable diff --git a/core/jni/android_hardware_photography_CameraMetadata.cpp b/core/jni/android_hardware_photography_CameraMetadata.cpp index fa363f3..5070d2c 100644 --- a/core/jni/android_hardware_photography_CameraMetadata.cpp +++ b/core/jni/android_hardware_photography_CameraMetadata.cpp @@ -16,6 +16,7 @@ */ // #define LOG_NDEBUG 0 +// #define LOG_NNDEBUG 0 #define LOG_TAG "CameraMetadata-JNI" #include <utils/Log.h> @@ -25,6 +26,16 @@ #include "android_runtime/AndroidRuntime.h" #include <camera/CameraMetadata.h> +#include <nativehelper/ScopedUtfChars.h> +#include <nativehelper/ScopedPrimitiveArray.h> + +#if defined(LOG_NNDEBUG) +#if !LOG_NNDEBUG +#define ALOGVV ALOGV +#endif +#else +#define ALOGVV(...) +#endif // fully-qualified class name #define CAMERA_METADATA_CLASS_NAME "android/hardware/photography/CameraMetadata" @@ -37,9 +48,70 @@ struct fields_t { static fields_t fields; +namespace { +struct Helpers { + static size_t getTypeSize(uint8_t type) { + if (type >= NUM_TYPES) { + ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type); + return static_cast<size_t>(-1); + } + + return camera_metadata_type_size[type]; + } + + static status_t updateAny(CameraMetadata *metadata, + uint32_t tag, + uint32_t type, + const void *data, + size_t dataBytes) { + + if (type >= NUM_TYPES) { + ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type); + return INVALID_OPERATION; + } + + size_t typeSize = getTypeSize(type); + + if (dataBytes % typeSize != 0) { + ALOGE("%s: Expected dataBytes (%ud) to be divisible by typeSize " + "(%ud)", __FUNCTION__, dataBytes, typeSize); + return BAD_VALUE; + } + + size_t dataCount = dataBytes / typeSize; + + switch(type) { +#define METADATA_UPDATE(runtime_type, compile_type) \ + case runtime_type: { \ + const compile_type *dataPtr = \ + static_cast<const compile_type*>(data); \ + return metadata->update(tag, dataPtr, dataCount); \ + } \ + + METADATA_UPDATE(TYPE_BYTE, uint8_t); + METADATA_UPDATE(TYPE_INT32, int32_t); + METADATA_UPDATE(TYPE_FLOAT, float); + METADATA_UPDATE(TYPE_INT64, int64_t); + METADATA_UPDATE(TYPE_DOUBLE, double); + METADATA_UPDATE(TYPE_RATIONAL, camera_metadata_rational_t); + + default: { + // unreachable + ALOGE("%s: Unreachable", __FUNCTION__); + return INVALID_OPERATION; + } + } + +#undef METADATA_UPDATE + } +}; +} // namespace {} + extern "C" { static void CameraMetadata_classInit(JNIEnv *env, jobject thiz); +static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName); +static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag); // Less safe access to native pointer. Does NOT throw any Java exceptions if NULL. static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) { @@ -140,6 +212,86 @@ static void CameraMetadata_swap(JNIEnv *env, jobject thiz, jobject other) { metadata->swap(*otherMetadata); } +static jbyteArray CameraMetadata_readValues(JNIEnv *env, jobject thiz, jint tag) { + ALOGV("%s (tag = %d)", __FUNCTION__, tag); + + CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); + if (metadata == NULL) return NULL; + + int tagType = get_camera_metadata_tag_type(tag); + if (tagType == -1) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Tag (%d) did not have a type", tag); + return NULL; + } + size_t tagSize = Helpers::getTypeSize(tagType); + + camera_metadata_entry entry = metadata->find(tag); + if (entry.count == 0) { + if (!metadata->exists(tag)) { + ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag); + return NULL; + } else { + // OK: we will return a 0-sized array. + ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__, + tag); + } + } + + jsize byteCount = entry.count * tagSize; + jbyteArray byteArray = env->NewByteArray(byteCount); + if (env->ExceptionCheck()) return NULL; + + // Copy into java array from native array + ScopedByteArrayRW arrayWriter(env, byteArray); + memcpy(arrayWriter.get(), entry.data.u8, byteCount); + + return byteArray; +} + +static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyteArray src) { + ALOGV("%s (tag = %d)", __FUNCTION__, tag); + + CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); + if (metadata == NULL) return; + + int tagType = get_camera_metadata_tag_type(tag); + if (tagType == -1) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Tag (%d) did not have a type", tag); + return; + } + size_t tagSize = Helpers::getTypeSize(tagType); + + status_t res; + + if (src == NULL) { + // If array is NULL, delete the entry + res = metadata->erase(tag); + } else { + // Copy from java array into native array + ScopedByteArrayRO arrayReader(env, src); + if (arrayReader.get() == NULL) return; + + res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag), + tagType, arrayReader.get(), arrayReader.size()); + } + + if (res == OK) { + return; + } else if (res == BAD_VALUE) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Src byte array was poorly formed"); + } else if (res == INVALID_OPERATION) { + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Internal error while trying to update metadata"); + } else { + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Unknown error (%d) while trying to update " + "metadata", res); + } +} + static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) { ALOGV("%s", __FUNCTION__); CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz); @@ -187,9 +339,17 @@ static void CameraMetadata_writeToParcel(JNIEnv *env, jobject thiz, jobject parc //------------------------------------------------- static JNINativeMethod gCameraMetadataMethods[] = { +// static methods { "nativeClassInit", "()V", (void *)CameraMetadata_classInit }, + { "nativeGetTagFromKey", + "(Ljava/lang/String;)I", + (void *)CameraMetadata_getTagFromKey }, + { "nativeGetTypeFromTag", + "(I)I", + (void *)CameraMetadata_getTypeFromTag }, +// instance methods { "nativeAllocate", "()J", (void*)CameraMetadata_allocate }, @@ -205,6 +365,13 @@ static JNINativeMethod gCameraMetadataMethods[] = { { "nativeSwap", "(L" CAMERA_METADATA_CLASS_NAME ";)V", (void *)CameraMetadata_swap }, + { "nativeReadValues", + "(I)[B", + (void *)CameraMetadata_readValues }, + { "nativeWriteValues", + "(I[B)V", + (void *)CameraMetadata_writeValues }, +// Parcelable interface { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", (void *)CameraMetadata_readFromParcel }, @@ -268,4 +435,99 @@ static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) { jclass clazz = env->FindClass(CAMERA_METADATA_CLASS_NAME); } + +static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) { + + ScopedUtfChars keyScoped(env, keyName); + const char *key = keyScoped.c_str(); + if (key == NULL) { + // exception thrown by ScopedUtfChars + return 0; + } + size_t keyLength = strlen(key); + + ALOGV("%s (key = '%s')", __FUNCTION__, key); + + // First, find the section by the longest string match + const char *section = NULL; + size_t sectionIndex = 0; + size_t sectionLength = 0; + for (size_t i = 0; i < ANDROID_SECTION_COUNT; ++i) { + const char *str = camera_metadata_section_names[i]; + ALOGVV("%s: Trying to match against section '%s'", + __FUNCTION__, str); + if (strstr(key, str) == key) { // key begins with the section name + size_t strLength = strlen(str); + + ALOGVV("%s: Key begins with section name", __FUNCTION__); + + // section name is the longest we've found so far + if (section == NULL || sectionLength < strLength) { + section = str; + sectionIndex = i; + sectionLength = strLength; + + ALOGVV("%s: Found new best section (idx %d)", __FUNCTION__, sectionIndex); + } + } + } + + // TODO: vendor ext + // TODO: Make above get_camera_metadata_section_from_name ? + + if (section == NULL) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Could not find section name for key '%s')", key); + return 0; + } else { + ALOGV("%s: Found matched section '%s' (%d)", + __FUNCTION__, section, sectionIndex); + } + + // Get the tag name component of the key + const char *keyTagName = key + sectionLength + 1; // x.y.z -> z + if (sectionLength + 1 >= keyLength) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Key length too short for key '%s')", key); + } + + // Match rest of name against the tag names in that section only + uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd) + tagBegin = camera_metadata_section_bounds[sectionIndex][0]; + tagEnd = camera_metadata_section_bounds[sectionIndex][1]; + + uint32_t tag; + for (tag = tagBegin; tag < tagEnd; ++tag) { + const char *tagName = get_camera_metadata_tag_name(tag); + + if (strcmp(keyTagName, tagName) == 0) { + ALOGV("%s: Found matched tag '%s' (%d)", + __FUNCTION__, tagName, tag); + break; + } + } + + // TODO: vendor ext + // TODO: Make above get_camera_metadata_tag_from_name ? + + if (tag == tagEnd) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Could not find tag name for key '%s')", key); + return 0; + } + + return tag; +} + +static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) { + int tagType = get_camera_metadata_tag_type(tag); + if (tagType == -1) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Tag (%d) did not have a type", tag); + return -1; + } + + return tagType; +} + } // extern "C" diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7ddb0dc..ea661a9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2259,6 +2259,16 @@ android:description="@string/permdesc_modifyNetworkAccounting" android:protectionLevel="signature|system" /> + <!-- Allows an application to mark traffic as from another user for per user routing. + Used by system wide services like media server that execute delegated network connections + for users. + @hide + --> + <permission android:name="android.permission.MARK_NETWORK_SOCKET" + android:label="@string/permlab_markNetworkSocket" + android:description="@string/permdesc_markNetworkSocket" + android:protectionLevel="signature|system" /> + <!-- C2DM permission. @hide Used internally. --> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 8c0da87..3796f73 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -941,8 +941,8 @@ <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ቦታ"</string> <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"አሰገባ"</string> <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"ሰርዝ"</string> - <string name="search_go" msgid="8298016669822141719">" ፈልግ"</string> - <string name="searchview_description_search" msgid="6749826639098512120">"ፈልግ"</string> + <string name="search_go" msgid="8298016669822141719">"ፍለጋ"</string> + <string name="searchview_description_search" msgid="6749826639098512120">"ፍለጋ"</string> <string name="searchview_description_query" msgid="5911778593125355124">"ጥያቄ ፍለጋ"</string> <string name="searchview_description_clear" msgid="1330281990951833033">"ጥያቄ አጥራ"</string> <string name="searchview_description_submit" msgid="2688450133297983542">"ጥያቄ አስረክብ"</string> @@ -1394,7 +1394,7 @@ <string name="description_target_camera" msgid="969071997552486814">"ካሜራ"</string> <string name="description_target_silent" msgid="893551287746522182">"ፀጥታ"</string> <string name="description_target_soundon" msgid="30052466675500172">"ድምፅ አብራ"</string> - <string name="description_target_search" msgid="3091587249776033139">"ፈልግ"</string> + <string name="description_target_search" msgid="3091587249776033139">"ፍለጋ"</string> <string name="description_target_unlock_tablet" msgid="3833195335629795055">"ላለመቆለፍ አንሸራት፡፡"</string> <string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"የይለፍ ቃል ቁልፎች ሲነገሩ ለመስማት የጆሮ ማዳመጫ ሰካ።"</string> <string name="keyboard_password_character_no_headset" msgid="2859873770886153678">"ነጥብ."</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0080ad9..42dddb4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1853,6 +1853,11 @@ <string name="permdesc_modifyNetworkAccounting">Allows the app to modify how network usage is accounted against apps. Not for use by normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_markNetworkSocket">modify socket marks</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_markNetworkSocket">Allows the app to modify socket marks for routing</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessNotifications">access notifications</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_accessNotifications">Allows the app to retrieve, examine, and clear notifications, including those posted by other apps.</string> diff --git a/docs/html/about/versions/jelly-bean.jd b/docs/html/about/versions/jelly-bean.jd index 71957be..5812f3d 100644 --- a/docs/html/about/versions/jelly-bean.jd +++ b/docs/html/about/versions/jelly-bean.jd @@ -5,7 +5,14 @@ tab2=Android 4.1 tab2.link=#android-41 @jd:body - +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <style> #android-41 {display:none;} diff --git a/docs/html/channels/io2013.jd b/docs/html/channels/io2013.jd index 977eb2f..b2bde31 100644 --- a/docs/html/channels/io2013.jd +++ b/docs/html/channels/io2013.jd @@ -1,7 +1,15 @@ fullpage=true page.title=Google I/O 13 @jd:body - +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> + <style> #ioplayer-frame { z-index:10; diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd index 00f4467..ab6bb1b 100644 --- a/docs/html/design/downloads/index.jd +++ b/docs/html/design/downloads/index.jd @@ -1,5 +1,13 @@ page.title=Downloads @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div class="layout-content-row"> <div class="layout-content-col span-9"> diff --git a/docs/html/design/index.jd b/docs/html/design/index.jd index 1e6b40c..d4ef07f 100644 --- a/docs/html/design/index.jd +++ b/docs/html/design/index.jd @@ -2,6 +2,14 @@ page.title=Design header.hide=1 footer.hide=1 @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <style> #landing-graphic-container { diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd index 7b5cb4a..f96e868 100644 --- a/docs/html/develop/index.jd +++ b/docs/html/develop/index.jd @@ -4,6 +4,14 @@ header.hide=1 carousel=1 tabbedList=1 @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <style> #noplayer-message { diff --git a/docs/html/distribute/googleplay/promote/badges.jd b/docs/html/distribute/googleplay/promote/badges.jd index 9a32921..93092bf 100644 --- a/docs/html/distribute/googleplay/promote/badges.jd +++ b/docs/html/distribute/googleplay/promote/badges.jd @@ -1,5 +1,13 @@ page.title=Google Play Badges @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <p itemprop="description">Google Play badges allow you to promote your app with official branding in your online ads, promotional materials, or anywhere else you want a link to your app.</p> diff --git a/docs/html/distribute/googleplay/promote/brand.jd b/docs/html/distribute/googleplay/promote/brand.jd index 265584f..a047b1f 100644 --- a/docs/html/distribute/googleplay/promote/brand.jd +++ b/docs/html/distribute/googleplay/promote/brand.jd @@ -1,6 +1,13 @@ page.title=Brand Guidelines @jd:body - +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <p>We encourage you to use the Android and Google Play brands with your Android app diff --git a/docs/html/distribute/googleplay/promote/index.jd b/docs/html/distribute/googleplay/promote/index.jd index 6882990..14f37c4 100644 --- a/docs/html/distribute/googleplay/promote/index.jd +++ b/docs/html/distribute/googleplay/promote/index.jd @@ -3,6 +3,14 @@ page.metaDescription=Raise the visibility of your apps in Google Play through de header.hide=0 footer.hide=0 @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <!-- <style> diff --git a/docs/html/distribute/googleplay/promote/linking.jd b/docs/html/distribute/googleplay/promote/linking.jd index 4fdc5db..014582a 100644 --- a/docs/html/distribute/googleplay/promote/linking.jd +++ b/docs/html/distribute/googleplay/promote/linking.jd @@ -1,5 +1,13 @@ page.title=Linking to Your Products @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div class="sidebox-wrapper"> <div class="sidebox"> diff --git a/docs/html/distribute/googleplay/publish/preparing.jd b/docs/html/distribute/googleplay/publish/preparing.jd index 5593f4f..dd35b25 100644 --- a/docs/html/distribute/googleplay/publish/preparing.jd +++ b/docs/html/distribute/googleplay/publish/preparing.jd @@ -1,6 +1,14 @@ page.title=Launch Checklist page.tags="publishing","launch","Google Play", "Developer Console" @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div id="qv-wrapper"><div id="qv"> <h2>Checklist</h2> diff --git a/docs/html/distribute/googleplay/quality/core.jd b/docs/html/distribute/googleplay/quality/core.jd index 9e23bcc..3fd221c 100644 --- a/docs/html/distribute/googleplay/quality/core.jd +++ b/docs/html/distribute/googleplay/quality/core.jd @@ -1,5 +1,13 @@ page.title=Core App Quality Guidelines @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div id="qv-wrapper"><div id="qv"> <h2>Quality Criteria</h2> diff --git a/docs/html/distribute/googleplay/quality/index.jd b/docs/html/distribute/googleplay/quality/index.jd index ef537b1..def42e5 100644 --- a/docs/html/distribute/googleplay/quality/index.jd +++ b/docs/html/distribute/googleplay/quality/index.jd @@ -1,5 +1,13 @@ page.title=App Quality @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <p>App quality directly influences the long-term success of your app—in terms of installs, user rating and reviews, engagement, and user retention. diff --git a/docs/html/distribute/googleplay/quality/tablet.jd b/docs/html/distribute/googleplay/quality/tablet.jd index 5a707be..c80c3cc 100644 --- a/docs/html/distribute/googleplay/quality/tablet.jd +++ b/docs/html/distribute/googleplay/quality/tablet.jd @@ -1,5 +1,13 @@ page.title=Tablet App Quality Checklist @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div id="qv-wrapper"><div id="qv"> <h2>Checklist</h2> diff --git a/docs/html/distribute/googleplay/spotlight/index.jd b/docs/html/distribute/googleplay/spotlight/index.jd index b83080e..88cdec4 100644 --- a/docs/html/distribute/googleplay/spotlight/index.jd +++ b/docs/html/distribute/googleplay/spotlight/index.jd @@ -3,7 +3,14 @@ walkthru=0 header.hide=0 @jd:body - +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <p>Android developers, their apps, and their successes with Android and Google Play. </p> diff --git a/docs/html/distribute/index.jd b/docs/html/distribute/index.jd index 54f9301..8e7c6e1 100644 --- a/docs/html/distribute/index.jd +++ b/docs/html/distribute/index.jd @@ -2,6 +2,14 @@ page.title=Distribute Apps header.hide=1 @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div class="marquee"> diff --git a/docs/html/distribute/promote/device-art.jd b/docs/html/distribute/promote/device-art.jd index 55b846e..58e183c 100644 --- a/docs/html/distribute/promote/device-art.jd +++ b/docs/html/distribute/promote/device-art.jd @@ -1,5 +1,13 @@ page.title=Device Art Generator @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <p>The device art generator allows you to quickly wrap your app screenshots in real device artwork. This provides better visual context for your app screenshots on your web site or in other diff --git a/docs/html/google/backup/signup.jd b/docs/html/google/backup/signup.jd index 550d590..f9ad600 100644 --- a/docs/html/google/backup/signup.jd +++ b/docs/html/google/backup/signup.jd @@ -226,7 +226,7 @@ onclick="onRegister(); return false;" >Register with Android Backup Service</a>< } else if ($("input#agree").is(':checked') && packagename.length && packagename != DEFAULT_TEXT) { - window.location = "https://play.google.com/apps/publish/v2/GetBackupApiKey?p=" + + window.location = "https://play.google.com/apps/publish/GetBackupApiKey?p=" + encodeURIComponent(packagename); } else { $("label#agreeLabel,label#pnameLabel").parent().stop().animate({color: "#258AAF"}, 200, diff --git a/docs/html/google/index.jd b/docs/html/google/index.jd index 4020cf4..095388e 100644 --- a/docs/html/google/index.jd +++ b/docs/html/google/index.jd @@ -1,6 +1,14 @@ page.title=Google Services header.hide=1 @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <style> div.landing-cell, diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd index 481a79c..0818514 100644 --- a/docs/html/google/play/billing/index.jd +++ b/docs/html/google/play/billing/index.jd @@ -1,5 +1,13 @@ page.title=Google Play In-app Billing @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <p>In-app Billing is a Google Play service that lets you sell digital content from inside your applications. You can use the service to sell a wide range of content, including downloadable diff --git a/docs/html/guide/components/index.jd b/docs/html/guide/components/index.jd index 87bae53..6ede873 100644 --- a/docs/html/guide/components/index.jd +++ b/docs/html/guide/components/index.jd @@ -4,6 +4,14 @@ page.landing.intro=Android's application framework lets you create extremely ric page.landing.image=images/develop/app_components.png @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div class="landing-docs"> diff --git a/docs/html/images/tools/studio_error_eventlog.png b/docs/html/images/tools/studio_error_eventlog.png Binary files differnew file mode 100644 index 0000000..909b285 --- /dev/null +++ b/docs/html/images/tools/studio_error_eventlog.png diff --git a/docs/html/images/tools/studio_error_gradle5.png b/docs/html/images/tools/studio_error_gradle5.png Binary files differnew file mode 100644 index 0000000..13de607 --- /dev/null +++ b/docs/html/images/tools/studio_error_gradle5.png diff --git a/docs/html/images/tools/studio_error_supportlib.png b/docs/html/images/tools/studio_error_supportlib.png Binary files differnew file mode 100644 index 0000000..603b54c --- /dev/null +++ b/docs/html/images/tools/studio_error_supportlib.png diff --git a/docs/html/index.jd b/docs/html/index.jd index d82deec..a972309 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -3,6 +3,14 @@ no_footer_links=true carousel=true page.metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers. @jd:body +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <div class="wrap"> diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd index 8825bcc..2e9c4b4 100644 --- a/docs/html/sdk/installing/studio.jd +++ b/docs/html/sdk/installing/studio.jd @@ -249,36 +249,36 @@ download (or continue to use) the <td>Windows</td> <td> <a onclick="return onDownload(this)" id="win-studio" - href="http://dl.google.com/android/studio/android-studio-bundle-130.687321-windows.exe"> - android-studio-bundle-130.677228-windows.exe + href="http://dl.google.com/android/studio/android-studio-bundle-130.737825-windows.exe"> + android-studio-bundle-130.737825-windows.exe </a> </td> - <td>393023485 bytes</td> - <td>3da987a9778b66edb68fb43d8b53bfcb</td> + <td>396091268 bytes</td> + <td>6da1bc8effa048c8ff669e4c484eb11f</td> </tr> <tr> <td><nobr>Mac OS X</nobr></td> <td> <a onclick="return onDownload(this)" id="mac-studio" - href="http://dl.google.com/android/studio/android-studio-bundle-130.687321-mac.dmg"> - android-studio-bundle-130.687321-mac.dmg + href="http://dl.google.com/android/studio/android-studio-bundle-130.737825-mac.dmg"> + android-studio-bundle-130.737825-mac.dmg </a> </td> - <td>379877697 bytes</td> - <td>eb5ca6c77f4a119595d941daeda58810</td> + <td>383326582 bytes</td> + <td>2959bc5039238d286670cc6225342b89</td> </tr> <tr> <td>Linux</td> <td> <a onclick="return onDownload(this)" id="linux-studio" - href="http://dl.google.com/android/studio/android-studio-bundle-130.687321-linux.tgz"> - android-studio-bundle-130.687321-linux.tgz + href="http://dl.google.com/android/studio/android-studio-bundle-130.737825-linux.tgz"> + android-studio-bundle-130.737825-linux.tgz </a> </td> - <td>406516375 bytes</td> - <td>6796d66de07c85b2822ca8d501a043c0</td> + <td>409935592 bytes</td> + <td>dcd13922f7cf577e3c852b224205d843</td> </tr> </table> @@ -286,6 +286,25 @@ download (or continue to use) the +<h2 id="Updating">Updating to 0.2.x</h2> + +<p>To update from Android Studio 0.1.x to 0.2.x, +follow the <a href="#Installing">installation instructions</a> below and replace your existing +installation.</p> + +<p class="caution"><strong>Caution:</strong> Replacing your existing installation of +Android Studio will remove any additional SDK packages you've installed, such as target +platforms, system images, and sample apps. To preserve these, copy them from your current +SDK directory under Android Studio to a temporary location +before installing the update. Then move them back once the update is complete. +If you fail to copy these packages, then you can instead download them again through +the Android SDK Manager.</p> + +<p>Also note that due to the update to Gradle 0.5, you will encounter errors when opening +existing projects. See the <a href="#Troubleshooting">Troubleshooting</a> notes below for +information about how to resolve them.</p> + + <h2 id="Installing">Installing Android Studio</h2> <ol> <li>Download the <strong>Android Studio</strong> package from above.</li> @@ -390,14 +409,78 @@ style="vertical-align:bottom;margin:0;height:19px" /> in the toolbar.</p> <h2 id="Revisions">Revisions</h2> -<p class="note"><strong>Note:</strong> Periodic updates are pushed to Android Studio -without requiring you to update from here. To manually check for updates, select -<strong>Help > Check for updates</strong> (on Mac, select <strong>Android Studio > -Check for updates</strong>).</p> +<p class="note"><strong>Note:</strong> <strong>There is not a patch update available from +0.1.9 to 0.2</strong>. To update from Android Studio 0.1.x to 0.2.x, you must +install a new Android Studio bundle from this page. The reason for that is that we have made +changes to the bundled SDK such that it includes a pre-configured local Maven repository +which can serve up the v4 support library and which is required for creating new projects.</p> + <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" + alt=""/>Android Studio v0.2.x</a> <em>(July 2013)</em> + </p> + + <div class="toggle-content-toggleme"> + <ul> + <li>Merged in the latest IntelliJ codebase changes. Includes fixes for issues reported by Studio users such as tweaks to Linux font sizes and font rendering.</li> + <li>Android Gradle plug-in updated to 0.5.0. + <p class="caution"><strong>Caution:</strong> This new version is not backwards compatible. + When opening a project that uses an older version of the plug-in, Studio will show an error + stating <strong>Gradle <project_name> project refresh failed.</strong> See <a + href="#Troubleshooting">Troubleshooting</a> below for details.</p> + <p>The updated Gradle plug-in includes the following changes:</p> + <ul> + <li>Fixed IDE model to contain the output file even if it's customized through the DSL. Also + fixed the DSL to get/set the output file on the variant object so that it's not necessary to + use <code>variant.packageApplication or variant.zipAlign</code></li> + <li>Fixed dependency resolution so that we resolved the combination of (default config, + build types, flavor(s)) together instead of separately.</li> + <li>Fixed dependency for tests of library project to properly include all the dependencies + of the library itself.</li> + <li>Fixed case where two dependencies have the same leaf name.</li> + <li>Fixed issue where Proguard rules file cannot be applied on flavors.</li> + </ul> + <p>All Gradle plugin release notes are available are here: <a href= + "http://tools.android.com/tech-docs/new-build-system" + >http://tools.android.com/tech-docs/new-build-system</a>.</p> + </li> + <li>Gradle errors from aapt no longer point to merged output files in the build/ folder, they + point back to the real source locations.</li> + <li>Parallel Builds. It's now possible to use Gradle's parallel builds. Please be aware that + parallel builds are in "incubation" (see <a + href="http://www.gradle.org/docs/current/userguide/gradle_command_line.html">Gradle's + documentation</a>.) This feature is off by default. To enable it, go to + <strong>Preferences</strong> > <strong>Compiler</strong> and check the box <em>Compile + independent modules in parallel</em>.</li> + <li>Further work on the new resource repository used for layout rendering, resource + folding in the editor, and more: + <ul> + <li>Basic support for .aar library dependencies (e.g. using a library without a local copy of + the sources). Still not working for resource XML validation and navigation in source editors. + </li> + <li>Cycle detection in resource references.</li> + <li>Quick Documentation (F1), which can show all translations of the string under the caret, + will now also show all resource overlays from the various Gradle flavors and build types, as + well as libraries. They are listed in reverse resource overlay order, with strikethrough on + the versions of the string that are masked.</li> + <li>Fixes to handle updating the merged resources when the set of module dependencies + change.</li> + <li>XML rendering fixes to properly handle character entity declarations and XML and unicode + escapes.</li> + </ul> + <li>Save screenshot support for the layout preview and layout editor windows.</li> + <li>Template bug fixes.</li> + <li>Lint bug fixes.</li> + <li>Various fixes for crash reports. Thank you, and keep filing crash reports!</li> + </ul> + </div> +</div> + +<div class="toggle-content closed"> + <p><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""/>Android Studio v0.1.x</a> <em>(May 2013)</em> </p> @@ -408,13 +491,76 @@ Check for updates</strong>).</p> </div> </div> +<p> </p> + +<p class="note"><strong>Note:</strong> Periodic updates are pushed to Android Studio +without requiring you to update from here. To manually check for updates, select +<strong>Help > Check for updates</strong> (on Mac, select <strong>Android Studio > +Check for updates</strong>).</p> + + + + +<h2 id="Troubleshooting">Troubleshooting</h2> + + +<div class="figure" style="width:330px"> +<img src="{@docRoot}images/tools/studio_error_gradle5.png" width="330"/> +<p class="img-caption"><strong>Figure 1.</strong> Error dialog when opening an existing project.</p> +</div> + +<h3>Error: Gradle project refresh failed</h3> + +<p>Android Studio 0.2.0 has updated the Gradle plug-in to 0.5.0, which is not backwards compatible. +When opening a project that uses an older version of the plug-in, Studio will display the error +shown in figure 1 in the upper right corner of the IDE. +To resolve the error, you must change the version of the Android Gradle plug-in to 0.5.0.</p> + +<ol> + <li>Click the link in the error dialog <strong>Search in build.gradle files</strong>. If the dialog +is no longer visible, click <strong>Event Log</strong> +<img src="{@docRoot}images/tools/studio_error_eventlog.png" +style="vertical-align:bottom;margin:0;height:19px"/> in the bottom-right corner of the IDE, +then click <strong>Search in build.gradle files</strong>.</li> + <li>Double-click the line under the <em>build.gradle</em> usage. For example: + <strong>classpath 'com.android.tools.build:gradle:0.4</strong>. This opens the project + <code>build.gradle</code> file.</li> + <li>Edit the <code>classpath</code> to change the gradle version to <code>0.5.+</code>. + For example: + <pre class="no-pretty-print"> +dependencies { + classpath 'com.android.tools.build:gradle:<strong>0.5.+</strong>' +} +</pre> + </li> + <li>Save the file and rebuild your project.</li> +</ol> +<div class="figure" style="width:330px"> +<img src="{@docRoot}images/tools/studio_error_supportlib.png" width="330"/> +<p class="img-caption"><strong>Figure 2.</strong> Error dialog when creating a new project +or opening a project using the support library.</p> +</div> +<h3>Error: Failed to import Gradle project</h3> +<p>If, after updating to Android Studio 0.2.x and creating or opening a project, you receive an +error stating <em>"Could not find any version that matches +com.android.support:support-v4:13.0.+"</em>, then you must install the <strong>Android Studio +Repository</strong>. This was likely caused because you're pointing Android Studio to an external +Android SDK location that does not have the new Maven repository included with Android Studio +0.2.x. This new Maven repository is used by the new build system for the Support Library, instead +of using the Support Library JAR files, so must be present in the SDK.</p> +<ol> + <li>Open the <strong>Android SDK Manager</strong>.</li> + <li>Expand the <strong>Extras</strong> directory +and install <strong>Android Studio Repository</strong>.</li> +</ol> + </div><!-- end main content --> @@ -430,7 +576,7 @@ Check for updates</strong>).</p> - + <script> var os; var bundlename; @@ -450,15 +596,15 @@ Check for updates</strong>).</p> if (os) { /* set up primary ACE download button */ $('#download-ide-button').show(); - $('#download-ide-button').append("Download Android Studio <span class='small'>v0.1.x</span>" - + "<br/><span class='small'>for " + os + "</span>"); + $('#download-ide-button').append("Download Android Studio <span class='small'>v0.2.x</span>" + + "<br/> <span class='small'>for " + os + "</span>"); $('#download-ide-button').click(function() {return onDownload(this,true);}).attr('href', bundlename); } else { $('.pax').show(); } - - + + function onDownload(link, button) { var $studioLink; @@ -470,7 +616,7 @@ Check for updates</strong>).</p> $studioLink = $(link); $("#downloadForRealz").html("Download " + $(link).text()); } - + $("#downloadForRealz").attr('href', $studioLink.attr('href')); $("#tos").fadeIn('fast'); @@ -493,10 +639,9 @@ Check for updates</strong>).</p> function onDownloadForRealz(link) { if ($("input#agree").is(':checked')) { - $("div.sdk-terms,#sdk-terms-form,.sdk-terms-intro").hide(); - $("#main").show(function() { - location.hash = "Installing"; - }); + $("#tos").hide(); + $("#main").show(); + location.hash = "Updating"; return true; } else { $("label#agreeLabel,#bitpicker input").parent().stop().animate({color: "#258AAF"}, 200, diff --git a/docs/html/tools/index.jd b/docs/html/tools/index.jd index f9d452c..e9094a7 100644 --- a/docs/html/tools/index.jd +++ b/docs/html/tools/index.jd @@ -1,6 +1,13 @@ page.title=Developer Tools @jd:body - +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <img src="{@docRoot}images/tools-home.png" style="float:right;" height="415" width="763" /> diff --git a/docs/html/training/index.jd b/docs/html/training/index.jd index 72ad018..82fbd16 100644 --- a/docs/html/training/index.jd +++ b/docs/html/training/index.jd @@ -3,7 +3,14 @@ page.trainingcourse=true page.metaDescription=Android Training provides a collection of classes that aim to help you build great apps for Android. Each class explains the steps required to solve a problem or implement a feature using code snippets and sample code for you to use in your apps. @jd:body - +<div id="butterbar-wrapper" > + <div id="butterbar" > + <div id="butterbar-message"> +<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform"> + Take the Android Developer Survey</a> + </div> + </div> +</div> <p>Welcome to Training for Android developers. Here you'll find sets of lessons within classes that describe how to accomplish a specific task with code samples you can re-use in your app. diff --git a/docs/html/training/sync-adapters/creating-authenticator.jd b/docs/html/training/sync-adapters/creating-authenticator.jd index 1b272e7..dfa9027 100644 --- a/docs/html/training/sync-adapters/creating-authenticator.jd +++ b/docs/html/training/sync-adapters/creating-authenticator.jd @@ -235,7 +235,7 @@ public class AuthenticatorService extends Service { <p> The following snippet shows the XML file for the authenticator you created previously: </p> -<pre>the +<pre> <?xml version="1.0" encoding="utf-8"?> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" diff --git a/docs/html/training/sync-adapters/index.jd b/docs/html/training/sync-adapters/index.jd index 1f7977b..b107cbe 100644 --- a/docs/html/training/sync-adapters/index.jd +++ b/docs/html/training/sync-adapters/index.jd @@ -11,7 +11,7 @@ startpage=true <h2>Dependencies and prerequisites</h2> <ul> - <li>Android 3.0 (API Level 11) or higher</li> + <li>Android 2.1 (API Level 7) or higher</li> </ul> <h2>You should also read</h2> diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index deccac1..aff4cd8 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -18,6 +18,7 @@ package android.graphics; import android.content.res.AssetManager; import android.content.res.Resources; +import android.os.Trace; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; @@ -436,12 +437,21 @@ public class BitmapFactory { if ((offset | length) < 0 || data.length < offset + length) { throw new ArrayIndexOutOfBoundsException(); } - Bitmap bm = nativeDecodeByteArray(data, offset, length, opts); - if (bm == null && opts != null && opts.inBitmap != null) { - throw new IllegalArgumentException("Problem decoding into existing bitmap"); + Bitmap bm; + + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); + try { + bm = nativeDecodeByteArray(data, offset, length, opts); + + if (bm == null && opts != null && opts.inBitmap != null) { + throw new IllegalArgumentException("Problem decoding into existing bitmap"); + } + setDensityFromOptions(bm, opts); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } - setDensityFromOptions(bm, opts); + return bm; } @@ -508,39 +518,43 @@ public class BitmapFactory { return null; } - // we need mark/reset to work properly - - if (!is.markSupported()) { - is = new BufferedInputStream(is, DECODE_BUFFER_SIZE); - } + Bitmap bm; - // so we can call reset() if a given codec gives up after reading up to - // this many bytes. FIXME: need to find out from the codecs what this - // value should be. - is.mark(1024); + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); + try { + // we need mark/reset to work properly + if (!is.markSupported()) { + is = new BufferedInputStream(is, DECODE_BUFFER_SIZE); + } - Bitmap bm; - boolean finish = true; + // so we can call reset() if a given codec gives up after reading up to + // this many bytes. FIXME: need to find out from the codecs what this + // value should be. + is.mark(1024); + + if (is instanceof AssetManager.AssetInputStream) { + final int asset = ((AssetManager.AssetInputStream) is).getAssetInt(); + bm = nativeDecodeAsset(asset, outPadding, opts); + } else { + // pass some temp storage down to the native code. 1024 is made up, + // but should be large enough to avoid too many small calls back + // into is.read(...) This number is not related to the value passed + // to mark(...) above. + byte [] tempStorage = null; + if (opts != null) tempStorage = opts.inTempStorage; + if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; + bm = nativeDecodeStream(is, tempStorage, outPadding, opts); + } - if (is instanceof AssetManager.AssetInputStream) { - final int asset = ((AssetManager.AssetInputStream) is).getAssetInt(); - bm = nativeDecodeAsset(asset, outPadding, opts); - } else { - // pass some temp storage down to the native code. 1024 is made up, - // but should be large enough to avoid too many small calls back - // into is.read(...) This number is not related to the value passed - // to mark(...) above. - byte [] tempStorage = null; - if (opts != null) tempStorage = opts.inTempStorage; - if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; - bm = nativeDecodeStream(is, tempStorage, outPadding, opts); - } + if (bm == null && opts != null && opts.inBitmap != null) { + throw new IllegalArgumentException("Problem decoding into existing bitmap"); + } - if (bm == null && opts != null && opts.inBitmap != null) { - throw new IllegalArgumentException("Problem decoding into existing bitmap"); + setDensityFromOptions(bm, opts); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } - setDensityFromOptions(bm, opts); return bm; } @@ -608,10 +622,7 @@ public class BitmapFactory { private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, Rect padding, Options opts); private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts); - private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts, - boolean applyScale, float scale); private static native Bitmap nativeDecodeByteArray(byte[] data, int offset, int length, Options opts); - private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad); private static native boolean nativeIsSeekable(FileDescriptor fd); } diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java index a837294..4e06448 100644 --- a/graphics/java/android/graphics/Matrix.java +++ b/graphics/java/android/graphics/Matrix.java @@ -267,13 +267,23 @@ public class Matrix { native_set(native_instance, src.native_instance); } } - + /** Returns true iff obj is a Matrix and its values equal our values. */ + @Override public boolean equals(Object obj) { - return obj != null && - obj instanceof Matrix && - native_equals(native_instance, ((Matrix)obj).native_instance); + //if (obj == this) return true; -- NaN value would mean matrix != itself + if (!(obj instanceof Matrix)) return false; + return native_equals(native_instance, ((Matrix)obj).native_instance); + } + + @Override + public int hashCode() { + // This should generate the hash code by performing some arithmetic operation on all + // the matrix elements -- our equals() does an element-by-element comparison, and we + // need to ensure that the hash code for two equal objects is the same. We're not + // really using this at the moment, so we take the easy way out. + return 44; } /** Set the matrix to identity */ @@ -512,7 +522,7 @@ public class Matrix { */ END (3); - // the native values must match those in SkMatrix.h + // the native values must match those in SkMatrix.h ScaleToFit(int nativeInt) { this.nativeInt = nativeInt; } @@ -535,7 +545,7 @@ public class Matrix { } return native_setRectToRect(native_instance, src, dst, stf.nativeInt); } - + // private helper to perform range checks on arrays of "points" private static void checkPointArrays(float[] src, int srcIndex, float[] dst, int dstIndex, @@ -598,7 +608,7 @@ public class Matrix { native_mapPoints(native_instance, dst, dstIndex, src, srcIndex, pointCount, true); } - + /** * Apply this matrix to the array of 2D vectors specified by src, and write * the transformed vectors into the array of vectors specified by dst. The @@ -620,7 +630,7 @@ public class Matrix { native_mapPoints(native_instance, dst, dstIndex, src, srcIndex, vectorCount, false); } - + /** * Apply this matrix to the array of 2D points specified by src, and write * the transformed points into the array of points specified by dst. The @@ -713,7 +723,7 @@ public class Matrix { public float mapRadius(float radius) { return native_mapRadius(native_instance, radius); } - + /** Copy 9 values from the matrix into the array. */ public void getValues(float[] values) { @@ -736,13 +746,14 @@ public class Matrix { native_setValues(native_instance, values); } + @Override public String toString() { StringBuilder sb = new StringBuilder(64); sb.append("Matrix{"); toShortString(sb); sb.append('}'); return sb.toString(); - + } public String toShortString() { @@ -780,13 +791,18 @@ public class Matrix { pw.print(values[5]); pw.print("]["); pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", "); pw.print(values[8]); pw.print(']'); - + } + @Override protected void finalize() throws Throwable { - finalizer(native_instance); + try { + finalizer(native_instance); + } finally { + super.finalize(); + } } - + /*package*/ final int ni() { return native_instance; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java index 3400434..8a2d347 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java @@ -18,7 +18,17 @@ package com.android.mediaframeworktest.unit; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; +import android.graphics.ImageFormat; import android.hardware.photography.CameraMetadata; +import android.hardware.photography.Rational; + +import static android.hardware.photography.CameraMetadata.*; + +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static org.junit.Assert.assertArrayEquals; /** * <pre> @@ -32,6 +42,21 @@ public class CameraMetadataTest extends junit.framework.TestCase { CameraMetadata mMetadata; Parcel mParcel; + // Sections + static final int ANDROID_COLOR_CORRECTION = 0; + static final int ANDROID_CONTROL = 1; + + // Section starts + static final int ANDROID_COLOR_CORRECTION_START = ANDROID_COLOR_CORRECTION << 16; + static final int ANDROID_CONTROL_START = ANDROID_CONTROL << 16; + + // Tags + static final int ANDROID_COLOR_CORRECTION_MODE = ANDROID_COLOR_CORRECTION_START; + static final int ANDROID_COLOR_CORRECTION_TRANSFORM = ANDROID_COLOR_CORRECTION_START + 1; + + static final int ANDROID_CONTROL_AE_ANTIBANDING_MODE = ANDROID_CONTROL_START; + static final int ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION = ANDROID_CONTROL_START + 1; + @Override public void setUp() { mMetadata = new CameraMetadata(); @@ -105,5 +130,310 @@ public class CameraMetadataTest extends junit.framework.TestCase { } catch (IllegalStateException e) { // good: we expect calling this method after close to fail } + + try { + mMetadata.readValues(/*tag*/0); + fail("Unreachable -- readValues after close should throw IllegalStateException"); + } catch (IllegalStateException e) { + // good: we expect calling this method after close to fail + } + + try { + mMetadata.writeValues(/*tag*/0, /*source*/new byte[] { 1,2,3 }); + fail("Unreachable -- readValues after close should throw IllegalStateException"); + } catch (IllegalStateException e) { + // good: we expect calling this method after close to fail + } + } + + @SmallTest + public void testGetTagFromKey() { + + // Test success + + assertEquals(ANDROID_COLOR_CORRECTION_MODE, + CameraMetadata.getTag("android.colorCorrection.mode")); + assertEquals(ANDROID_COLOR_CORRECTION_TRANSFORM, + CameraMetadata.getTag("android.colorCorrection.transform")); + assertEquals(ANDROID_CONTROL_AE_ANTIBANDING_MODE, + CameraMetadata.getTag("android.control.aeAntibandingMode")); + assertEquals(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, + CameraMetadata.getTag("android.control.aeExposureCompensation")); + + // Test failures + + try { + CameraMetadata.getTag(null); + fail("A null key should throw NPE"); + } catch(NullPointerException e) { + } + + try { + CameraMetadata.getTag("android.control"); + fail("A section name only should not be a valid key"); + } catch(IllegalArgumentException e) { + } + + try { + CameraMetadata.getTag("android.control.thisTagNameIsFakeAndDoesNotExist"); + fail("A valid section with an invalid tag name should not be a valid key"); + } catch(IllegalArgumentException e) { + } + + try { + CameraMetadata.getTag("android"); + fail("A namespace name only should not be a valid key"); + } catch(IllegalArgumentException e) { + } + + try { + CameraMetadata.getTag("this.key.is.definitely.invalid"); + fail("A completely fake key name should not be valid"); + } catch(IllegalArgumentException e) { + } + } + + @SmallTest + public void testGetTypeFromTag() { + assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_MODE)); + assertEquals(TYPE_FLOAT, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM)); + assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE)); + assertEquals(TYPE_INT32, + CameraMetadata.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION)); + + try { + CameraMetadata.getNativeType(0xDEADF00D); + fail("No type should exist for invalid tag 0xDEADF00D"); + } catch(IllegalArgumentException e) { + } + } + + @SmallTest + public void testReadWriteValues() { + final byte ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; + byte[] valueResult; + + assertEquals(0, mMetadata.getEntryCount()); + assertEquals(true, mMetadata.isEmpty()); + + // + // android.colorCorrection.mode (single enum byte) + // + + assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE)); + + // Write 0 values + mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] {}); + + // Read 0 values + valueResult = mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE); + assertNotNull(valueResult); + assertEquals(0, valueResult.length); + + assertEquals(1, mMetadata.getEntryCount()); + assertEquals(false, mMetadata.isEmpty()); + + // Write 1 value + mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] { + ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY + }); + + // Read 1 value + valueResult = mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE); + assertNotNull(valueResult); + assertEquals(1, valueResult.length); + assertEquals(ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY, valueResult[0]); + + assertEquals(1, mMetadata.getEntryCount()); + assertEquals(false, mMetadata.isEmpty()); + + // + // android.colorCorrection.transform (3x3 matrix) + // + + final float[] transformMatrix = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + byte[] transformMatrixAsByteArray = new byte[transformMatrix.length * 4]; + ByteBuffer transformMatrixByteBuffer = + ByteBuffer.wrap(transformMatrixAsByteArray).order(ByteOrder.nativeOrder()); + for (float f : transformMatrix) + transformMatrixByteBuffer.putFloat(f); + + // Read + assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM)); + mMetadata.writeValues(ANDROID_COLOR_CORRECTION_TRANSFORM, transformMatrixAsByteArray); + + // Write + assertArrayEquals(transformMatrixAsByteArray, + mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM)); + + assertEquals(2, mMetadata.getEntryCount()); + assertEquals(false, mMetadata.isEmpty()); + + // Erase + mMetadata.writeValues(ANDROID_COLOR_CORRECTION_TRANSFORM, null); + assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM)); + assertEquals(1, mMetadata.getEntryCount()); + } + + private static <T> void assertArrayEquals(T expected, T actual) { + assertEquals(Array.getLength(expected), Array.getLength(actual)); + + int len = Array.getLength(expected); + for (int i = 0; i < len; ++i) { + assertEquals(Array.get(expected, i), Array.get(actual, i)); + } + } + + private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T value) { + Key<T> key = new Key<T>(keyStr, type); + assertNull(mMetadata.get(key)); + mMetadata.set(key, value); + assertEquals(value, mMetadata.get(key)); + } + + private <T> void checkKeyGetAndSetArray(String keyStr, Class<T> type, T value) { + Key<T> key = new Key<T>(keyStr, type); + assertNull(mMetadata.get(key)); + mMetadata.set(key, value); + assertArrayEquals(value, mMetadata.get(key)); + } + + @SmallTest + public void testReadWritePrimitive() { + // int32 (single) + checkKeyGetAndSet("android.control.aeExposureCompensation", Integer.TYPE, 0xC0FFEE); + + // byte (single) + checkKeyGetAndSet("android.flash.maxEnergy", Byte.TYPE, (byte)6); + + // int64 (single) + checkKeyGetAndSet("android.flash.firingTime", Long.TYPE, 0xABCD12345678FFFFL); + + // float (single) + checkKeyGetAndSet("android.lens.aperture", Float.TYPE, Float.MAX_VALUE); + + // double (single) -- technically double x 3, but we fake it + checkKeyGetAndSet("android.jpeg.gpsCoordinates", Double.TYPE, Double.MAX_VALUE); + + // rational (single) + checkKeyGetAndSet("android.sensor.baseGainFactor", Rational.class, new Rational(1, 2)); + + /** + * Weirder cases, that don't map 1:1 with the native types + */ + + // bool (single) -- with TYPE_BYTE + checkKeyGetAndSet("android.control.aeLock", Boolean.TYPE, true); + + // integer (single) -- with TYPE_BYTE + checkKeyGetAndSet("android.control.aePrecaptureTrigger", Integer.TYPE, 6); + } + + @SmallTest + public void testReadWritePrimitiveArray() { + // int32 (n) + checkKeyGetAndSetArray("android.sensor.info.availableSensitivities", int[].class, + new int[] { + 0xC0FFEE, 0xDEADF00D + }); + + // byte (n) + checkKeyGetAndSetArray("android.statistics.faceScores", byte[].class, new byte[] { + 1, 2, 3, 4 + }); + + // int64 (n) + checkKeyGetAndSetArray("android.scaler.availableProcessedMinDurations", long[].class, + new long[] { + 0xABCD12345678FFFFL, 0x1234ABCD5678FFFFL, 0xFFFF12345678ABCDL + }); + + // float (n) + checkKeyGetAndSetArray("android.lens.info.availableApertures", float[].class, + new float[] { + Float.MAX_VALUE, Float.MIN_NORMAL, Float.MIN_VALUE + }); + + // double (n) -- in particular double x 3 + checkKeyGetAndSetArray("android.jpeg.gpsCoordinates", double[].class, + new double[] { + Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE + }); + + // rational (n) -- in particular rational x 9 + checkKeyGetAndSetArray("android.sensor.calibrationTransform1", Rational[].class, + new Rational[] { + new Rational(1, 2), new Rational(3, 4), new Rational(5, 6), + new Rational(7, 8), new Rational(9, 10), new Rational(10, 11), + new Rational(12, 13), new Rational(14, 15), new Rational(15, 16) + }); + + /** + * Weirder cases, that don't map 1:1 with the native types + */ + + // bool (n) -- with TYPE_BYTE + checkKeyGetAndSetArray("android.control.aeLock", boolean[].class, new boolean[] { + true, false, true + }); + + + // integer (n) -- with TYPE_BYTE + checkKeyGetAndSetArray("android.control.aeAvailableModes", int[].class, new int[] { + 1, 2, 3, 4 + }); + } + + private enum ColorCorrectionMode { + TRANSFORM_MATRIX, + FAST, + HIGH_QUALITY + } + + private enum AeAntibandingMode { + OFF, + _50HZ, + _60HZ, + AUTO + } + + // TODO: special values for the enum. + private enum AvailableFormat { + RAW_SENSOR, + YV12, + YCrCb_420_SP, + IMPLEMENTATION_DEFINED, + YCbCr_420_888, + BLOB + } + + @SmallTest + public void testReadWriteEnum() { + // byte (single) + checkKeyGetAndSet("android.colorCorrection.mode", ColorCorrectionMode.class, + ColorCorrectionMode.HIGH_QUALITY); + + // byte (single) + checkKeyGetAndSet("android.control.aeAntibandingMode", AeAntibandingMode.class, + AeAntibandingMode.AUTO); + + // byte (n) + checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes", + AeAntibandingMode[].class, new AeAntibandingMode[] { + AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ, + AeAntibandingMode.AUTO + }); + + /** + * Stranger cases that don't use byte enums + */ + // int (n) + checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class, + new AvailableFormat[] { + AvailableFormat.RAW_SENSOR, + AvailableFormat.YV12, + AvailableFormat.IMPLEMENTATION_DEFINED + }); + } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java new file mode 100644 index 0000000..c15d030 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.unit; + +import android.test.suitebuilder.annotation.SmallTest; +import android.hardware.photography.Rational; + +/** + * <pre> + * adb shell am instrument \ + * -e class 'com.android.mediaframeworktest.unit.RationalTest' \ + * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner + * </pre> + */ +public class RationalTest extends junit.framework.TestCase { + @SmallTest + public void testConstructor() { + + // Simple case + Rational r = new Rational(1, 2); + assertEquals(1, r.getNumerator()); + assertEquals(2, r.getDenominator()); + + // Denominator negative + r = new Rational(-1, 2); + assertEquals(-1, r.getNumerator()); + assertEquals(2, r.getDenominator()); + + // Numerator negative + r = new Rational(1, -2); + assertEquals(-1, r.getNumerator()); + assertEquals(2, r.getDenominator()); + + // Both negative + r = new Rational(-1, -2); + assertEquals(1, r.getNumerator()); + assertEquals(2, r.getDenominator()); + + // Dividing by zero is not allowed + try { + r = new Rational(1, 0); + fail("Expected Rational constructor to throw an IllegalArgumentException"); + } catch(IllegalArgumentException e) { + } + } + + @SmallTest + public void testGcd() { + Rational r = new Rational(1, 2); + assertEquals(1, r.gcd()); + + Rational twoThirds = new Rational(2, 3); + assertEquals(1, twoThirds.gcd()); + + Rational moreComplicated2 = new Rational(5*78, 7*78); + assertEquals(78, moreComplicated2.gcd()); + + Rational oneHalf = new Rational(-1, 2); + assertEquals(1, oneHalf.gcd()); + + twoThirds = new Rational(-2, 3); + assertEquals(1, twoThirds.gcd()); + } + + @SmallTest + public void testEquals() { + Rational r = new Rational(1, 2); + assertEquals(1, r.getNumerator()); + assertEquals(2, r.getDenominator()); + + assertEquals(r, r); + assertFalse(r.equals(null)); + assertFalse(r.equals(new Object())); + + Rational twoThirds = new Rational(2, 3); + assertFalse(r.equals(twoThirds)); + assertFalse(twoThirds.equals(r)); + + Rational fourSixths = new Rational(4, 6); + assertEquals(twoThirds, fourSixths); + assertEquals(fourSixths, twoThirds); + + Rational moreComplicated = new Rational(5*6*7*8*9, 1*2*3*4*5); + Rational moreComplicated2 = new Rational(5*6*7*8*9*78, 1*2*3*4*5*78); + assertEquals(moreComplicated, moreComplicated2); + assertEquals(moreComplicated2, moreComplicated); + + // Ensure negatives are fine + twoThirds = new Rational(-2, 3); + fourSixths = new Rational(-4, 6); + assertEquals(twoThirds, fourSixths); + assertEquals(fourSixths, twoThirds); + + moreComplicated = new Rational(-5*6*7*8*9, 1*2*3*4*5); + moreComplicated2 = new Rational(-5*6*7*8*9*78, 1*2*3*4*5*78); + assertEquals(moreComplicated, moreComplicated2); + assertEquals(moreComplicated2, moreComplicated); + + } +}
\ No newline at end of file diff --git a/packages/BackupRestoreConfirmation/res/values-nl/strings.xml b/packages/BackupRestoreConfirmation/res/values-nl/strings.xml index d525429..7bb0e9b 100644 --- a/packages/BackupRestoreConfirmation/res/values-nl/strings.xml +++ b/packages/BackupRestoreConfirmation/res/values-nl/strings.xml @@ -26,9 +26,9 @@ <string name="deny_restore_button_label" msgid="1724367334453104378">"Niet herstellen"</string> <string name="current_password_text" msgid="8268189555578298067">"Geef hieronder uw huidige back-upwachtwoord op:"</string> <string name="device_encryption_restore_text" msgid="1570864916855208992">"Geef hieronder uw wachtwoord voor apparaatcodering op."</string> - <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geef hieronder uw wachtwoord voor apparaatcodering op. Dit wordt ook gebruikt om het back-uparchief te coderen."</string> + <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geef hieronder uw wachtwoord voor apparaatversleuteling op. Dit wordt ook gebruikt om het back-uparchief te versleutelen."</string> <string name="backup_enc_password_text" msgid="4981585714795233099">"Geef een wachtwoord op dat u wilt gebruiken voor het coderen van de gegevens van de volledige back-up. Als u dit leeg laat, wordt uw huidige back-upwachtwoord gebruikt:"</string> - <string name="backup_enc_password_optional" msgid="1350137345907579306">"Als u de gegevens van de volledige back-up wilt coderen, geeft u daarvoor hieronder een wachtwoord op:"</string> + <string name="backup_enc_password_optional" msgid="1350137345907579306">"Als u de gegevens van de volledige back-up wilt versleutelen, geeft u daarvoor hieronder een wachtwoord op:"</string> <string name="restore_enc_password_text" msgid="6140898525580710823">"Als deze herstelgegevens zijn gecodeerd, geeft u hieronder het wachtwoord op:"</string> <string name="toast_backup_started" msgid="550354281452756121">"Back-up starten..."</string> <string name="toast_backup_ended" msgid="3818080769548726424">"Back-up voltooid"</string> diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 58fc8e8..6343d0a 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -71,7 +71,7 @@ import libcore.io.ErrnoException; import libcore.io.IoUtils; import libcore.io.Libcore; import libcore.io.Streams; -import libcore.io.StructStatFs; +import libcore.io.StructStatVfs; /* * This service copies a downloaded apk to a file passed in as @@ -246,7 +246,7 @@ public class DefaultContainerService extends IntentService { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); try { - final StructStatFs stat = Libcore.os.statfs(path); + final StructStatVfs stat = Libcore.os.statvfs(path); final long totalSize = stat.f_blocks * stat.f_bsize; final long availSize = stat.f_bavail * stat.f_bsize; return new long[] { totalSize, availSize }; diff --git a/packages/Keyguard/res/values-am/strings.xml b/packages/Keyguard/res/values-am/strings.xml index 325163d..fc9d946 100644 --- a/packages/Keyguard/res/values-am/strings.xml +++ b/packages/Keyguard/res/values-am/strings.xml @@ -85,7 +85,7 @@ <string name="description_target_camera" msgid="969071997552486814">"ካሜራ"</string> <string name="description_target_silent" msgid="893551287746522182">"ፀጥታ"</string> <string name="description_target_soundon" msgid="30052466675500172">"ድምፅ አብራ"</string> - <string name="description_target_search" msgid="3091587249776033139">"ፈልግ"</string> + <string name="description_target_search" msgid="3091587249776033139">"ፍለጋ"</string> <string name="description_direction_up" msgid="7169032478259485180">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ላይ አንሸራትት።"</string> <string name="description_direction_down" msgid="5087739728639014595">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ታች አንሸራትት።"</string> <string name="description_direction_left" msgid="7207478719805562165">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ግራ አንሸራትት።"</string> diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 0b8b028..262b7ee 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -33,6 +33,7 @@ import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -4789,8 +4790,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { ActivityInfo ai = null; ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser( intent, - PackageManager.MATCH_DEFAULT_ONLY, - UserHandle.USER_CURRENT); + PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, + getCurrentUserId()); if (info != null) { ai = info.activityInfo; } @@ -4805,6 +4806,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { return null; } + private int getCurrentUserId() { + try { + UserInfo user = ActivityManagerNative.getDefault().getCurrentUser(); + return user != null ? user.id : UserHandle.USER_NULL; + } catch (RemoteException e) { + // noop + } + return UserHandle.USER_NULL; + } + void startDockOrHome() { awakenDreams(); diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 65749b3..fab091f 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -1188,6 +1188,11 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { mBuiltInKeyboardId = device->id; } + // 'Q' key support = cheap test of whether this is an alpha-capable kbd + if (hasKeycodeLocked(device, AKEYCODE_Q)) { + device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; + } + // See if this device has a DPAD. if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && @@ -1205,14 +1210,6 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { } } - // 'Q' key support = cheap test of whether this is an alpha-capable kbd. Many gamepads will - // report a broader set of HID usages than they need, however, so we only want to mark this - // device as a keyboard if it is not a gamepad. - if (hasKeycodeLocked(device, AKEYCODE_Q) && - !(device->classes & INPUT_DEVICE_CLASS_GAMEPAD)) { - device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; - } - // Disable kernel key repeat since we handle it ourselves unsigned int repeatRate[] = {0,0}; if (ioctl(fd, EVIOCSREP, repeatRate)) { diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 231cfe1..c6c9845 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -47,7 +47,6 @@ import android.location.LocationRequest; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; @@ -222,6 +221,9 @@ public class LocationManagerService extends ILocationManager.Stub { AppOpsManager.Callback callback = new AppOpsManager.Callback() { public void opChanged(int op, String packageName) { synchronized (mLock) { + for (Receiver receiver : mReceivers.values()) { + receiver.updateMonitoring(true); + } applyAllProviderRequirementsLocked(); } } @@ -460,6 +462,7 @@ public class LocationManagerService extends ILocationManager.Stub { final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>(); + boolean mOpMonitoring; int mPendingBroadcasts; PowerManager.WakeLock mWakeLock; @@ -477,6 +480,8 @@ public class LocationManagerService extends ILocationManager.Stub { mPid = pid; mPackageName = packageName; + updateMonitoring(true); + // construct/configure wakelock mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); mWakeLock.setWorkSource(new WorkSource(mUid, mPackageName)); @@ -512,6 +517,21 @@ public class LocationManagerService extends ILocationManager.Stub { return s.toString(); } + public void updateMonitoring(boolean allow) { + if (!mOpMonitoring) { + if (allow) { + mOpMonitoring = mAppOps.startOpNoThrow(AppOpsManager.OP_MONITOR_LOCATION, + mUid, mPackageName) == AppOpsManager.MODE_ALLOWED; + } + } else { + if (!allow || mAppOps.checkOpNoThrow(AppOpsManager.OP_MONITOR_LOCATION, + mUid, mPackageName) != AppOpsManager.MODE_ALLOWED) { + mAppOps.finishOp(AppOpsManager.OP_MONITOR_LOCATION, mUid, mPackageName); + mOpMonitoring = false; + } + } + } + public boolean isListener() { return mListener != null; } @@ -1366,6 +1386,8 @@ public class LocationManagerService extends ILocationManager.Stub { } } + receiver.updateMonitoring(false); + // Record which providers were associated with this listener HashSet<String> providers = new HashSet<String>(); HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 39ce0c6..65006e5 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1000,7 +1000,6 @@ public final class ActivityManagerService extends ActivityManagerNative static final int USER_SWITCH_TIMEOUT_MSG = 36; static final int IMMERSIVE_MODE_LOCK_MSG = 37; static final int PERSIST_URI_GRANTS = 38; - static final int SET_FOCUSED_STACK = 39; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1493,18 +1492,6 @@ public final class ActivityManagerService extends ActivityManagerNative writeGrantedUriPermissions(); break; } - case SET_FOCUSED_STACK: { - synchronized (ActivityManagerService.this) { - ActivityStack stack = mStackSupervisor.getStack(msg.arg1); - if (stack != null) { - ActivityRecord r = stack.topRunningActivityLocked(null); - if (r != null) { - setFocusedActivityLocked(r); - } - } - } - break; - } } } }; @@ -2081,7 +2068,26 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void setFocusedStack(int stackId) { if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: stackId=" + stackId); - mHandler.obtainMessage(SET_FOCUSED_STACK, stackId, 0).sendToTarget(); + synchronized (ActivityManagerService.this) { + ActivityStack stack = mStackSupervisor.getStack(stackId); + if (stack != null) { + ActivityRecord r = stack.topRunningActivityLocked(null); + if (r != null) { + setFocusedActivityLocked(r); + } + } + } + } + + @Override + public void notifyActivityDrawn(IBinder token) { + if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token); + synchronized (this) { + ActivityRecord r= mStackSupervisor.isInAnyStackLocked(token); + if (r != null) { + r.task.stack.notifyActivityDrawnLocked(r); + } + } } final void applyUpdateLockStateLocked(ActivityRecord r) { @@ -8048,7 +8054,26 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void convertToOpaque(IBinder token) { + public void convertFromTranslucent(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + if (r.changeWindowTranslucency(true)) { + mWindowManager.setAppFullscreen(token, true); + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void convertToTranslucent(IBinder token) { final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -8056,8 +8081,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (r == null) { return; } - if (r.convertToOpaque()) { - mWindowManager.setAppFullscreen(token); + if (r.changeWindowTranslucency(false)) { + r.task.stack.convertToTranslucent(r); + mWindowManager.setAppFullscreen(token, false); mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); } } @@ -10963,23 +10989,37 @@ public final class ActivityManagerService extends ActivityManagerNative } final static class MemItem { + final boolean isProc; final String label; final String shortLabel; final long pss; final int id; + final boolean hasActivities; ArrayList<MemItem> subitems; + public MemItem(String _label, String _shortLabel, long _pss, int _id, + boolean _hasActivities) { + isProc = true; + label = _label; + shortLabel = _shortLabel; + pss = _pss; + id = _id; + hasActivities = _hasActivities; + } + public MemItem(String _label, String _shortLabel, long _pss, int _id) { + isProc = false; label = _label; shortLabel = _shortLabel; pss = _pss; id = _id; + hasActivities = false; } } - static final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items, - boolean sort) { - if (sort) { + static final void dumpMemItems(PrintWriter pw, String prefix, String tag, + ArrayList<MemItem> items, boolean sort, boolean isCompact) { + if (sort && !isCompact) { Collections.sort(items, new Comparator<MemItem>() { @Override public int compare(MemItem lhs, MemItem rhs) { @@ -10995,9 +11035,19 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<items.size(); i++) { MemItem mi = items.get(i); - pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label); + if (!isCompact) { + pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label); + } else if (mi.isProc) { + pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel); + pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss); + pw.println(mi.hasActivities ? ",a" : ",e"); + } else { + pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(","); + pw.println(mi.pss); + } if (mi.subitems != null) { - dumpMemItems(pw, prefix + " ", mi.subitems, true); + dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems, + true, isCompact); } } } @@ -11042,6 +11092,12 @@ public final class ActivityManagerService extends ActivityManagerNative "Backup", "A Services", "Home", "Previous", "B Services", "Cached" }; + static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { + "sys", "pers", "fore", + "vis", "percept", "heavy", + "backup", "servicea", "home", "prev", + "serviceb", "cached" + }; final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, String[] args, boolean brief, @@ -11049,6 +11105,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean dumpDetails = false; boolean dumpDalvik = false; boolean oomOnly = false; + boolean isCompact = false; int opti = 0; while (opti < args.length) { @@ -11062,12 +11119,15 @@ public final class ActivityManagerService extends ActivityManagerNative dumpDalvik = true; } else if ("-d".equals(opt)) { dumpDalvik = true; + } else if ("-c".equals(opt)) { + isCompact = true; } else if ("--oom".equals(opt)) { oomOnly = true; } else if ("-h".equals(opt)) { - pw.println("meminfo dump options: [-a] [--oom] [process]"); + pw.println("meminfo dump options: [-a] [-d] [-c] [--oom] [process]"); pw.println(" -a: include all available information for each process."); pw.println(" -d: include dalvik details when dumping process details."); + pw.println(" -c: dump in a compact machine-parseable representation."); pw.println(" --oom: only show processes organized by oom adj."); pw.println("If [process] is specified it can be the name or "); pw.println("pid of a specific process to dump."); @@ -11090,10 +11150,9 @@ public final class ActivityManagerService extends ActivityManagerNative dumpDetails = true; } - if (isCheckinRequest) { + if (isCheckinRequest || isCompact) { // short checkin version - pw.println(uptime + "," + realtime); - pw.flush(); + pw.print("time,"); pw.print(uptime); pw.print(","); pw.println(realtime); } else { pw.println("Applications Memory Usage (kB):"); pw.println("Uptime: " + uptime + " Realtime: " + realtime); @@ -11115,20 +11174,24 @@ public final class ActivityManagerService extends ActivityManagerNative Debug.MemoryInfo mi = null; for (int i = procs.size() - 1 ; i >= 0 ; i--) { - ProcessRecord r = procs.get(i); - IApplicationThread thread; - int oomAdj; + final ProcessRecord r = procs.get(i); + final IApplicationThread thread; + final int pid; + final int oomAdj; + final boolean hasActivities; synchronized (this) { thread = r.thread; + pid = r.pid; oomAdj = r.getSetAdjWithServices(); + hasActivities = r.hasActivities; } if (thread != null) { if (!isCheckinRequest && dumpDetails) { - pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **"); - pw.flush(); + pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **"); } if (dumpDetails) { try { + pw.flush(); mi = null; mi = thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs); } catch (RemoteException e) { @@ -11142,9 +11205,9 @@ public final class ActivityManagerService extends ActivityManagerNative mi = new Debug.MemoryInfo(); } if (!brief && !oomOnly) { - Debug.getMemoryInfo(r.pid, mi); + Debug.getMemoryInfo(pid, mi); } else { - mi.dalvikPss = (int)Debug.getPss(r.pid); + mi.dalvikPss = (int)Debug.getPss(pid); } } @@ -11159,8 +11222,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (!isCheckinRequest && mi != null) { totalPss += myTotalPss; - MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")", - r.processName, myTotalPss, 0); + MemItem pssItem = new MemItem(r.processName + " (pid " + pid + + (hasActivities ? " / activities)" : ")"), + r.processName, myTotalPss, pid, hasActivities); procMems.add(pssItem); nativePss += mi.nativePss; @@ -11177,7 +11241,7 @@ public final class ActivityManagerService extends ActivityManagerNative } for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) { - if (r.setAdj <= DUMP_MEM_OOM_ADJ[oomIndex] + if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex] || oomIndex == (oomPss.length-1)) { oomPss[oomIndex] += myTotalPss; if (oomProcs[oomIndex] == null) { @@ -11205,7 +11269,8 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); for (int j=0; j<oomPss.length; j++) { if (oomPss[j] != 0) { - String label = DUMP_MEM_OOM_LABEL[j]; + String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] + : DUMP_MEM_OOM_LABEL[j]; MemItem item = new MemItem(label, label, oomPss[j], DUMP_MEM_OOM_ADJ[j]); item.subitems = oomProcs[j]; @@ -11275,29 +11340,45 @@ public final class ActivityManagerService extends ActivityManagerNative } } - if (!brief && !oomOnly) { + if (!brief && !oomOnly && !isCompact) { pw.println(); pw.println("Total PSS by process:"); - dumpMemItems(pw, " ", procMems, true); + dumpMemItems(pw, " ", "proc", procMems, true, isCompact); pw.println(); } - pw.println("Total PSS by OOM adjustment:"); - dumpMemItems(pw, " ", oomMems, false); + if (!isCompact) { + pw.println("Total PSS by OOM adjustment:"); + } + dumpMemItems(pw, " ", "oom", oomMems, false, isCompact); if (!brief && !oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; - out.println(); - out.println("Total PSS by category:"); - dumpMemItems(out, " ", catMems, true); + if (!isCompact) { + out.println(); + out.println("Total PSS by category:"); + } + dumpMemItems(out, " ", "cat", catMems, true, isCompact); + } + if (!isCompact) { + pw.println(); } - pw.println(); if (!brief) { MemInfoReader memInfo = new MemInfoReader(); memInfo.readMemInfo(); - pw.print("Total RAM: "); pw.print(memInfo.getTotalSize()/1024); pw.println(" kB"); - pw.print(" Free RAM: "); pw.print(cachedPss + (memInfo.getCachedSize()/1024) - + (memInfo.getFreeSize()/1024)); pw.println(" kB"); + if (!isCompact) { + pw.print("Total RAM: "); pw.print(memInfo.getTotalSize()/1024); + pw.println(" kB"); + pw.print(" Free RAM: "); pw.print(cachedPss + (memInfo.getCachedSize()/1024) + + (memInfo.getFreeSize()/1024)); pw.println(" kB"); + } else { + pw.print("ram,"); pw.print(memInfo.getTotalSize()/1024); pw.print(","); + pw.print(cachedPss + (memInfo.getCachedSize()/1024) + + (memInfo.getFreeSize()/1024)); pw.print(","); + pw.println(totalPss - cachedPss); + } + } + if (!isCompact) { + pw.print(" Used PSS: "); pw.print(totalPss - cachedPss); pw.println(" kB"); } - pw.print(" Used PSS: "); pw.print(totalPss - cachedPss); pw.println(" kB"); if (!brief) { final int[] SINGLE_LONG_FORMAT = new int[] { Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG @@ -11318,11 +11399,18 @@ public final class ActivityManagerService extends ActivityManagerNative Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile", SINGLE_LONG_FORMAT, null, longOut, null); long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024; - if (sharing != 0 || shared != 0 || unshared != 0 || voltile != 0) { - pw.print(" KSM: "); pw.print(sharing); pw.print(" kB saved from shared "); - pw.print(shared); pw.println(" kB"); - pw.print(" "); pw.print(unshared); pw.print(" kB unshared; "); - pw.print(voltile); pw.println(" kB volatile"); + if (!isCompact) { + if (sharing != 0 || shared != 0 || unshared != 0 || voltile != 0) { + pw.print(" KSM: "); pw.print(sharing); + pw.print(" kB saved from shared "); + pw.print(shared); pw.println(" kB"); + pw.print(" "); pw.print(unshared); pw.print(" kB unshared; "); + pw.print(voltile); pw.println(" kB volatile"); + } + } else { + pw.print("ksm,"); pw.print(sharing); pw.print(","); + pw.print(shared); pw.print(","); pw.print(unshared); pw.print(","); + pw.println(voltile); } } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 4f09407..2ae9ed6 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -17,6 +17,7 @@ package com.android.server.am; import android.os.Trace; +import com.android.internal.R.styleable; import com.android.internal.app.ResolverActivity; import com.android.server.AttributeCache; import com.android.server.am.ActivityStack.ActivityState; @@ -505,19 +506,23 @@ final class ActivityRecord { } } - boolean convertToOpaque() { - if (fullscreen) { + boolean changeWindowTranslucency(boolean toOpaque) { + if (fullscreen == toOpaque) { return false; } - - AttributeCache.Entry ent = AttributeCache.instance().get(packageName, - realTheme, com.android.internal.R.styleable.Window); - if (ent != null && !ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsFloating, false)) { - fullscreen = true; - ++task.numFullscreen; + AttributeCache.Entry ent = + AttributeCache.instance().get(packageName, realTheme, styleable.Window); + if (ent == null + || !ent.array.getBoolean(styleable.Window_windowIsTranslucent, false) + || ent.array.getBoolean(styleable.Window_windowIsFloating, false)) { + return false; } - return fullscreen; + + // Keep track of the number of fullscreen activities in this task. + task.numFullscreen += toOpaque ? +1 : -1; + + fullscreen = toOpaque; + return true; } void putInHistory() { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 98b3ce9..be03ee3 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -118,6 +118,10 @@ final class ActivityStack { // is being started. static final boolean SHOW_APP_STARTING_PREVIEW = true; + // How long to wait for all background Activities to redraw following a call to + // convertToTranslucent(). + static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000; + enum ActivityState { INITIALIZING, RESUMED, @@ -184,6 +188,16 @@ final class ActivityStack { */ ActivityRecord mLastStartedActivity = null; + // The topmost Activity passed to convertToTranslucent(). When non-null it means we are + // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they + // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the + // Activity in mTranslucentActivityWaiting is notified via + // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last + // background activity being drawn then the same call will be made with a true value. + ActivityRecord mTranslucentActivityWaiting = null; + ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = + new ArrayList<ActivityRecord>(); + /** * Set when we know we are going to be calling updateConfiguration() * soon, so want to skip intermediate config checks. @@ -215,6 +229,7 @@ final class ActivityStack { static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3; static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4; static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5; + static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6; static class ScheduleDestroyArgs { final ProcessRecord mOwner; @@ -285,7 +300,12 @@ final class ActivityStack { synchronized (mService) { destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason); } - } + } break; + case TRANSLUCENT_TIMEOUT_MSG: { + synchronized (mService) { + notifyActivityDrawnLocked(null); + } + } break; } } } @@ -952,6 +972,16 @@ final class ActivityStack { TAG, "ensureActivitiesVisible behind " + top + " configChanges=0x" + Integer.toHexString(configChanges)); + if (mTranslucentActivityWaiting != top) { + mUndrawnActivitiesBelowTopTranslucent.clear(); + if (mTranslucentActivityWaiting != null) { + // Call the callback with a timeout indication. + notifyActivityDrawnLocked(null); + mTranslucentActivityWaiting = null; + } + mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG); + } + // If the top activity is not fullscreen, then we need to // make sure any activities under it are now visible. boolean aboveTop = true; @@ -1018,6 +1048,9 @@ final class ActivityStack { if (DEBUG_VISBILITY) Slog.v( TAG, "Making visible and scheduling visibility: " + r); try { + if (mTranslucentActivityWaiting != null) { + mUndrawnActivitiesBelowTopTranslucent.add(r); + } mWindowManager.setAppVisibility(r.appToken, true); r.sleeping = false; r.app.pendingUiClean = true; @@ -1091,6 +1124,42 @@ final class ActivityStack { return showHomeBehindStack; } + void convertToTranslucent(ActivityRecord r) { + mTranslucentActivityWaiting = r; + mUndrawnActivitiesBelowTopTranslucent.clear(); + mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT); + } + + /** + * Called as activities below the top translucent activity are redrawn. When the last one is + * redrawn notify the top activity by calling + * {@link Activity#onTranslucentConversionComplete}. + * + * @param r The most recent background activity to be drawn. Or, if r is null then a timeout + * occurred and the activity will be notified immediately. + */ + void notifyActivityDrawnLocked(ActivityRecord r) { + if ((r == null) + || (mUndrawnActivitiesBelowTopTranslucent.remove(r) && + mUndrawnActivitiesBelowTopTranslucent.isEmpty())) { + // The last undrawn activity below the top has just been drawn. If there is an + // opaque activity at the top, notify it that it can become translucent safely now. + final ActivityRecord waitingActivity = mTranslucentActivityWaiting; + mTranslucentActivityWaiting = null; + mUndrawnActivitiesBelowTopTranslucent.clear(); + mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG); + + if (waitingActivity != null && waitingActivity.app != null && + waitingActivity.app.thread != null) { + try { + waitingActivity.app.thread.scheduleTranslucentConversionComplete( + waitingActivity.appToken, r != null); + } catch (RemoteException e) { + } + } + } + } + /** * Ensure that the top activity in the stack is resumed. * diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 63f91ac..d39798d 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -2475,8 +2475,7 @@ public final class ActivityStackSupervisor { public void handleMessage(Message msg) { switch (msg.what) { case IDLE_TIMEOUT_MSG: { - if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: Callers=" + - Debug.getCallers(4)); + if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj); if (mService.mDidDexOpt) { mService.mDidDexOpt = false; Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); @@ -2489,6 +2488,7 @@ public final class ActivityStackSupervisor { activityIdleInternal((ActivityRecord)msg.obj); } break; case IDLE_NOW_MSG: { + if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj); activityIdleInternal((ActivityRecord)msg.obj); } break; case RESUME_TOP_ACTIVITY_MSG: { diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 0e7513c..5238bd1 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -410,7 +410,7 @@ public final class BroadcastQueue { } } if (r.appOp != AppOpsManager.OP_NONE) { - int mode = mService.mAppOpsService.checkOperation(r.appOp, + int mode = mService.mAppOpsService.noteOperation(r.appOp, filter.receiverList.uid, filter.packageName); if (mode != AppOpsManager.MODE_ALLOWED) { if (DEBUG_BROADCAST) Slog.v(TAG, @@ -717,7 +717,7 @@ public final class BroadcastQueue { } } if (r.appOp != AppOpsManager.OP_NONE) { - int mode = mService.mAppOpsService.checkOperation(r.appOp, + int mode = mService.mAppOpsService.noteOperation(r.appOp, info.activityInfo.applicationInfo.uid, info.activityInfo.packageName); if (mode != AppOpsManager.MODE_ALLOWED) { if (DEBUG_BROADCAST) Slog.v(TAG, diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index 5585ac8..6a72e44 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -39,7 +39,7 @@ final class ProcessList { // This is a process only hosting activities that are not visible, // so it can be killed without any disruption. static final int CACHED_APP_MAX_ADJ = 15; - static int CACHED_APP_MIN_ADJ = 9; + static final int CACHED_APP_MIN_ADJ = 9; // The B list of SERVICE_ADJ -- these are the old and decrepit // services that aren't as shiny and interesting as the ones in the A list. diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 14f4102..21a6ff0 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -465,8 +465,10 @@ final class ProcessRecord { } public int getSetAdjWithServices() { - if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ && hasStartedServices) { - return ProcessList.SERVICE_B_ADJ; + if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) { + if (hasStartedServices) { + return ProcessList.SERVICE_B_ADJ; + } } return setAdj; } diff --git a/services/java/com/android/server/pm/KeySetManager.java b/services/java/com/android/server/pm/KeySetManager.java index 3480b19..93992c2 100644 --- a/services/java/com/android/server/pm/KeySetManager.java +++ b/services/java/com/android/server/pm/KeySetManager.java @@ -69,7 +69,7 @@ public class KeySetManager { mPackages = packages; } - /* + /** * Determine if a package is signed by the given KeySet. * * Returns false if the package was not signed by all the @@ -94,7 +94,7 @@ public class KeySetManager { } } - /* + /** * This informs the system that the given package has defined a KeySet * in its manifest that a) contains the given keys and b) is named * alias by that package. @@ -116,7 +116,7 @@ public class KeySetManager { } } - /* + /** * Similar to the above, this informs the system that the given package * was signed by the provided KeySet. */ @@ -153,10 +153,9 @@ public class KeySetManager { } } - /* - * Fetches the stable identifier associated with the given KeySet. - * - * Returns KEYSET_NOT_FOUND if the KeySet... wasn't found. + /** + * Fetches the stable identifier associated with the given KeySet. Returns + * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found. */ public long getIdByKeySet(KeySet ks) { synchronized (mLockObject) { @@ -174,10 +173,11 @@ public class KeySetManager { return KEYSET_NOT_FOUND; } - /* + /** * Fetches the KeySet corresponding to the given stable identifier. * - * Returns KEYSET_NOT_FOUND if the identifier doesn't identify a KeySet. + * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't + * identify a {@link KeySet}. */ public KeySet getKeySetById(long id) { synchronized (mLockObject) { @@ -185,7 +185,7 @@ public class KeySetManager { } } - /* + /** * Fetches the KeySet that a given package refers to by the provided alias. * * If the package isn't known to us, throws an IllegalArgumentException. @@ -205,10 +205,9 @@ public class KeySetManager { } } - /* - * Fetches all the known KeySets that signed the given package. - * - * If the package is unknown to us, throws an IllegalArgumentException. + /** + * Fetches all the known {@link KeySet KeySets} that signed the given + * package. Returns {@code null} if package is unknown. */ public Set<KeySet> getSigningKeySetsByPackageName(String packageName) { synchronized (mLockObject) { @@ -227,16 +226,16 @@ public class KeySetManager { } } - /* + /** * Creates a new KeySet corresponding to the given keys. * - * If the PublicKeys aren't known to the system, this adds them. Otherwise, - * they're deduped. + * If the {@link PublicKey PublicKeys} aren't known to the system, this + * adds them. Otherwise, they're deduped. * * If the KeySet isn't known to the system, this adds that and creates the * mapping to the PublicKeys. If it is known, then it's deduped. * - * Throws if the provided set is null. + * Throws if the provided set is {@code null}. */ private KeySet addKeySetLocked(Set<PublicKey> keys) { if (keys == null) { @@ -267,7 +266,7 @@ public class KeySetManager { return ks; } - /* + /** * Adds the given PublicKey to the system, deduping as it goes. */ private long addPublicKeyLocked(PublicKey key) { @@ -284,7 +283,7 @@ public class KeySetManager { return id; } - /* + /** * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs. * * Returns KEYSET_NOT_FOUND if there isn't one. @@ -299,7 +298,7 @@ public class KeySetManager { return KEYSET_NOT_FOUND; } - /* + /** * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND. */ private long getIdForPublicKeyLocked(PublicKey k) { @@ -314,7 +313,7 @@ public class KeySetManager { return PUBLIC_KEY_NOT_FOUND; } - /* + /** * Gets an unused stable identifier for a KeySet. */ private long getFreeKeySetIDLocked() { @@ -322,7 +321,7 @@ public class KeySetManager { return lastIssuedKeySetId; } - /* + /** * Same as above, but for public keys. */ private long getFreePublicKeyIdLocked() { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index ee2ef37..50d267f 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -25,6 +25,7 @@ import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import android.app.AppOpsManager; import android.util.TimeUtils; import android.view.IWindowId; + import com.android.internal.app.IBatteryStats; import com.android.internal.policy.PolicyManager; import com.android.internal.policy.impl.PhoneWindowManager; @@ -4117,10 +4118,11 @@ public class WindowManagerService extends IWindowManager.Stub } } - public void setAppFullscreen(IBinder token) { + public void setAppFullscreen(IBinder token, boolean toOpaque) { AppWindowToken atoken = findAppWindowToken(token); if (atoken != null) { - atoken.appFullscreen = true; + atoken.appFullscreen = toOpaque; + requestTraversal(); } } @@ -7020,6 +7022,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int CLIENT_FREEZE_TIMEOUT = 30; public static final int TAP_OUTSIDE_STACK = 31; + public static final int NOTIFY_ACTIVITY_DRAWN = 32; @Override public void handleMessage(Message msg) { @@ -7452,6 +7455,13 @@ public class WindowManagerService extends IWindowManager.Stub } } } + break; + case NOTIFY_ACTIVITY_DRAWN: + try { + mActivityManager.notifyActivityDrawn((IBinder) msg.obj); + } catch (RemoteException e) { + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG, "handleMessage: exit"); @@ -8759,6 +8769,7 @@ public class WindowManagerService extends IWindowManager.Stub + " interesting=" + numInteresting + " drawn=" + wtoken.numDrawnWindows); wtoken.allDrawn = true; + mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget(); } } } diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml index d7838eb..1a9288c 100644 --- a/tests/CanvasCompare/res/layout/manual_layout.xml +++ b/tests/CanvasCompare/res/layout/manual_layout.xml @@ -64,7 +64,7 @@ </LinearLayout> </LinearLayout> - <com.android.test.hwuicompare.NearestImageView + <ImageView android:id="@+id/compare_image_view" android:layout_width="@dimen/layer_width_double" android:layout_height="@dimen/layer_height_double" diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java index 400dff0..405ff65 100644 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java +++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java @@ -82,6 +82,7 @@ public class ManualActivity extends CompareActivity { mCompareImageView.setImageBitmap(mCompareBitmap); break; } + mCompareImageView.getDrawable().setFilterBitmap(false); mCompareImageView.invalidate(); } diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java deleted file mode 100644 index 542b55a..0000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.test.hwuicompare; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PaintFlagsDrawFilter; -import android.util.AttributeSet; -import android.widget.ImageView; - -public class NearestImageView extends ImageView { - public NearestImageView(Context context) { - super(context); - } - - public NearestImageView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public NearestImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - final PaintFlagsDrawFilter mFilter = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); - - @Override - public void onDraw(Canvas canvas) { - canvas.setDrawFilter(mFilter); - super.onDraw(canvas); - canvas.setDrawFilter(null); - } -}
\ No newline at end of file diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml index 5483f64..9a399d0 100644 --- a/tests/TransitionTests/AndroidManifest.xml +++ b/tests/TransitionTests/AndroidManifest.xml @@ -233,6 +233,13 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:label="CrossfadeImage" + android:name=".CrossfadeImage"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> </application> diff --git a/tests/TransitionTests/res/layout/crossfade_image.xml b/tests/TransitionTests/res/layout/crossfade_image.xml new file mode 100644 index 0000000..c46327a --- /dev/null +++ b/tests/TransitionTests/res/layout/crossfade_image.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/self_portrait_square_100" + android:onClick="sendMessage" + android:id="@+id/contact_picture"/> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/self_portrait_square_100" + android:onClick="sendMessage" + android:id="@+id/contact_picture1"/> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/self_portrait_square_100" + android:onClick="sendMessage" + android:id="@+id/contact_picture2"/> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java new file mode 100644 index 0000000..28e055f --- /dev/null +++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.transitiontests; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.transition.Crossfade; +import android.view.transition.Move; +import android.view.transition.Scene; +import android.view.transition.Transition; +import android.view.transition.TransitionGroup; +import android.view.transition.TransitionManager; +import android.widget.ImageView; + +public class CrossfadeImage extends Activity { + ViewGroup mSceneRoot; + static int mCurrentScene; + Scene mScene1, mScene2; + TransitionManager mTransitionManager; + boolean mExpanded = false; + Transition mTransition; + ImageView mImageView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.crossfade_image); + + ViewGroup container = (ViewGroup) findViewById(R.id.container); + mSceneRoot = container; + + mImageView = (ImageView) findViewById(R.id.contact_picture); + mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + + Crossfade mCrossfade = new Crossfade(); + mCrossfade.setTargetIds(R.id.contact_picture); + + TransitionGroup group = new TransitionGroup(); + group.setDuration(1500); + group.addTransitions(mCrossfade, new Move()); + mTransition = group; + } + + public void sendMessage(View view) { + TransitionManager.beginDelayedTransition(mSceneRoot, mTransition); + if (mExpanded) { + mImageView.setImageResource(R.drawable.self_portrait_square_100); + } else { + mImageView.setImageResource(R.drawable.self_portrait_square_200); + } + mExpanded = !mExpanded; + } +} diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index a095648..858fbcc 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -1783,6 +1783,14 @@ public class WifiStateMachine extends StateMachine { // TODO: Remove this comment when the driver is fixed. setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false); mWifiNative.setPowerSave(false); + + /* P2p discovery breaks dhcp, shut it down in order to get through this */ + Message msg = new Message(); + msg.what = WifiP2pService.BLOCK_DISCOVERY; + msg.arg1 = WifiP2pService.ENABLED; + msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE; + msg.obj = mDhcpStateMachine; + mWifiP2pChannel.sendMessage(msg); } @@ -1809,6 +1817,8 @@ public class WifiStateMachine extends StateMachine { setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true); mWifiNative.setPowerSave(true); + mWifiP2pChannel.sendMessage(WifiP2pService.BLOCK_DISCOVERY, WifiP2pService.DISABLED); + // Set the coexistence mode back to its default value mWifiNative.setBluetoothCoexistenceMode( mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); @@ -3012,7 +3022,6 @@ public class WifiStateMachine extends StateMachine { switch (message.what) { case DhcpStateMachine.CMD_PRE_DHCP_ACTION: handlePreDhcpSetup(); - mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE); break; case DhcpStateMachine.CMD_POST_DHCP_ACTION: handlePostDhcpSetup(); diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index 4cfc4ac..68a082a 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -166,6 +166,16 @@ public class WifiP2pService extends IWifiP2pManager.Stub { public static final int SET_MIRACAST_MODE = BASE + 14; + // During dhcp (and perhaps other times) we can't afford to drop packets + // but Discovery will switch our channel enough we will. + // msg.arg1 = ENABLED for blocking, DISABLED for resumed. + // msg.arg2 = msg to send when blocked + // msg.obj = StateMachine to send to when blocked + public static final int BLOCK_DISCOVERY = BASE + 15; + + public static final int ENABLED = 1; + public static final int DISABLED = 0; + private final boolean mP2pSupported; private WifiP2pDevice mThisDevice = new WifiP2pDevice(); @@ -182,6 +192,15 @@ public class WifiP2pService extends IWifiP2pManager.Stub { * broadcasts */ private boolean mDiscoveryStarted; + /* Track whether servcice/peer discovery is blocked in favor of other wifi actions + * (notably dhcp) + */ + private boolean mDiscoveryBlocked; + + /* + * remember if we were in a scan when it had to be stopped + */ + private boolean mDiscoveryPostponed = false; private NetworkInfo mNetworkInfo; @@ -479,6 +498,20 @@ public class WifiP2pService extends IWifiP2pManager.Stub { AsyncChannel ac = new AsyncChannel(); ac.connect(mContext, getHandler(), message.replyTo); break; + case BLOCK_DISCOVERY: + mDiscoveryBlocked = (message.arg1 == ENABLED ? true : false); + // always reset this - we went to a state that doesn't support discovery so + // it would have stopped regardless + mDiscoveryPostponed = false; + if (mDiscoveryBlocked) { + try { + StateMachine m = (StateMachine)message.obj; + m.sendMessage(message.arg2); + } catch (Exception e) { + loge("unable to send BLOCK_DISCOVERY response: " + e); + } + } + break; case WifiP2pManager.DISCOVER_PEERS: replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, WifiP2pManager.BUSY); @@ -851,7 +884,33 @@ public class WifiP2pService extends IWifiP2pManager.Stub { } break; } + case BLOCK_DISCOVERY: + boolean blocked = (message.arg1 == ENABLED ? true : false); + if (mDiscoveryBlocked == blocked) break; + mDiscoveryBlocked = blocked; + if (blocked && mDiscoveryStarted) { + mWifiNative.p2pStopFind(); + mDiscoveryPostponed = true; + } + if (!blocked && mDiscoveryPostponed) { + mDiscoveryPostponed = false; + mWifiNative.p2pFind(DISCOVER_TIMEOUT_S); + } + if (blocked) { + try { + StateMachine m = (StateMachine)message.obj; + m.sendMessage(message.arg2); + } catch (Exception e) { + loge("unable to send BLOCK_DISCOVERY response: " + e); + } + } + break; case WifiP2pManager.DISCOVER_PEERS: + if (mDiscoveryBlocked) { + replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, + WifiP2pManager.BUSY); + break; + } // do not send service discovery request while normal find operation. clearSupplicantServiceRequest(); if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) { @@ -874,6 +933,11 @@ public class WifiP2pService extends IWifiP2pManager.Stub { } break; case WifiP2pManager.DISCOVER_SERVICES: + if (mDiscoveryBlocked) { + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.BUSY); + break; + } if (DBG) logd(getName() + " discover services"); if (!updateSupplicantServiceRequest()) { replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, |