diff options
45 files changed, 1411 insertions, 722 deletions
diff --git a/api/current.txt b/api/current.txt index cf1d923df6b6..e5156e8328c4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3139,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); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a23f3044307c..b24aeb017109 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; @@ -1616,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; } @@ -1715,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) { @@ -1773,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); } @@ -1871,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); @@ -1941,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); @@ -1954,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); @@ -1986,6 +1792,7 @@ public final class ActivityThread { } ActivityThread() { + mResourcesManager = ResourcesManager.getInstance(); } public ApplicationThread getApplicationThread() @@ -1998,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; @@ -2031,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()); @@ -3434,7 +3237,7 @@ public final class ActivityThread { } private void handleSetCoreSettings(Bundle coreSettings) { - synchronized (mPackages) { + synchronized (mResourcesManager) { mCoreSettings = coreSettings; } } @@ -3524,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(); @@ -3679,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) { @@ -3740,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; @@ -3869,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)); @@ -3963,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; @@ -4080,7 +3787,7 @@ public final class ActivityThread { int configDiff = 0; - synchronized (mPackages) { + synchronized (mResourcesManager) { if (mPendingConfiguration != null) { if (!mPendingConfiguration.isOtherSeqNewer(config)) { config = mPendingConfiguration; @@ -4096,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(); } @@ -4348,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); @@ -5060,6 +4767,7 @@ public final class ActivityThread { mSystemThread = system; if (!system) { ViewRootImpl.addFirstDrawHandler(new Runnable() { + @Override public void run() { ensureJitEnabled(); } @@ -5096,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 || @@ -5113,8 +4822,10 @@ public final class ActivityThread { } } } + @Override public void onLowMemory() { } + @Override public void onTrimMemory(int level) { } }); @@ -5134,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 32bb71e33388..de94bb7c0401 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/ContextImpl.java b/core/java/android/app/ContextImpl.java index a5106e45223d..6a0fbd59bee3 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/LoadedApk.java b/core/java/android/app/LoadedApk.java index 573a6aad6ded..05d3a47ee98b 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 000000000000..e9693dd8a9c3 --- /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 c24e0ee2bac9..6483cd979dff 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 000000000000..53e0f2c00e0d --- /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/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 8e0935da2186..12646bde77f2 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1803,8 +1803,8 @@ public abstract class BatteryStats implements Parcelable { for (int i=0; i<timers.size(); i++) { TimerEntry timer = timers.get(i); sb.setLength(0); - sb.append(" Wake lock #"); - sb.append(timer.mId); + sb.append(" Wake lock "); + UserHandle.formatUid(sb, timer.mId); sb.append(" "); sb.append(timer.mName); printWakeLock(sb, timer.mTimer, batteryRealtime, null, which, ": "); @@ -1822,8 +1822,11 @@ public abstract class BatteryStats implements Parcelable { } Uid u = uidStats.valueAt(iu); - - pw.println(prefix + " #" + uid + ":"); + + pw.print(prefix); + pw.print(" "); + UserHandle.formatUid(pw, uid); + pw.println(":"); boolean uidActivity = false; long mobileRxBytes = u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, which); diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index d2052539ccb9..6e693a41230f 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -168,8 +168,11 @@ public final class UserHandle implements Parcelable { if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { sb.append('i'); sb.append(appId - Process.FIRST_ISOLATED_UID); - } else { + } else if (appId >= Process.FIRST_APPLICATION_UID) { sb.append('a'); + sb.append(appId - Process.FIRST_APPLICATION_UID); + } else { + sb.append('s'); sb.append(appId); } } @@ -190,8 +193,11 @@ public final class UserHandle implements Parcelable { if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { pw.print('i'); pw.print(appId - Process.FIRST_ISOLATED_UID); - } else { + } else if (appId >= Process.FIRST_APPLICATION_UID) { pw.print('a'); + pw.print(appId - Process.FIRST_APPLICATION_UID); + } else { + pw.print('s'); pw.print(appId); } } diff --git a/core/java/android/view/GraphicBuffer.java b/core/java/android/view/GraphicBuffer.java index b4576f367b94..30c077c2e879 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 281bd7e9a2b7..d9e4600d42db 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/transition/Fade.java b/core/java/android/view/transition/Fade.java index 3c5b6fad622e..c2aa90beba76 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 d0e61ea28c4b..313e33e601bd 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/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index c4406ac66d69..05a8dc825e12 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/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 4e213247e503..78389c5121f2 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -432,6 +432,17 @@ public class InputMethodUtils { } } + public static CharSequence getImeAndSubtypeDisplayName(Context context, InputMethodInfo imi, + InputMethodSubtype subtype) { + final CharSequence imiLabel = imi.loadLabel(context.getPackageManager()); + return subtype != null + ? TextUtils.concat(subtype.getDisplayName(context, + imi.getPackageName(), imi.getServiceInfo().applicationInfo), + (TextUtils.isEmpty(imiLabel) ? + "" : " - " + imiLabel)) + : imiLabel; + } + /** * Utility class for putting and getting settings for InputMethod * TODO: Move all putters and getters of settings to this class. diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 464ae2f5e8dd..35b76dd2213f 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/docs/html/sdk/installing/studio-tips.jd b/docs/html/sdk/installing/studio-tips.jd index a686efdfad24..12d252710a28 100644 --- a/docs/html/sdk/installing/studio-tips.jd +++ b/docs/html/sdk/installing/studio-tips.jd @@ -10,6 +10,8 @@ page.title=Android Studio Tips and Tricks >Eclipse Compatibility Mode</a></li> <li><a href="http://confluence.jetbrains.com/display/IntelliJIDEA/FAQ+on+Migrating+to+IntelliJ+IDEA" class="external-link" >FAQ on Migrating</a></li> + <li><a href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html" + class="external-link">Adding a Backend to Your App In Android Studio</a></li> </ul> </div> </div> diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java index a837294ecb82..4e064486f0d4 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/packages/SystemUI/res/anim/priority_alert_enter.xml b/packages/SystemUI/res/anim/heads_up_enter.xml index 4fd6a7c9d2f7..4fd6a7c9d2f7 100644 --- a/packages/SystemUI/res/anim/priority_alert_enter.xml +++ b/packages/SystemUI/res/anim/heads_up_enter.xml diff --git a/packages/SystemUI/res/anim/priority_alert_exit.xml b/packages/SystemUI/res/anim/heads_up_exit.xml index 05c144a1720d..05c144a1720d 100644 --- a/packages/SystemUI/res/anim/priority_alert_exit.xml +++ b/packages/SystemUI/res/anim/heads_up_exit.xml diff --git a/packages/SystemUI/res/drawable/intruder_row_bg.xml b/packages/SystemUI/res/drawable/heads_up_notification_row_bg.xml index 1c7c9c454602..59d9fcfc7a16 100644 --- a/packages/SystemUI/res/drawable/intruder_row_bg.xml +++ b/packages/SystemUI/res/drawable/heads_up_notification_row_bg.xml @@ -15,6 +15,7 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android" - android:exitFadeDuration="@android:integer/config_mediumAnimTime"> - <item android:state_pressed="true" android:drawable="@drawable/intruder_bg_pressed" /> + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> + <item android:state_pressed="true" + android:drawable="@drawable/heads_up_notification_bg_pressed" /> </selector> diff --git a/packages/SystemUI/res/drawable/intruder_window_bg.9.png b/packages/SystemUI/res/drawable/heads_up_window_bg.9.png Binary files differindex caad1691b226..caad1691b226 100644 --- a/packages/SystemUI/res/drawable/intruder_window_bg.9.png +++ b/packages/SystemUI/res/drawable/heads_up_window_bg.9.png diff --git a/packages/SystemUI/res/layout/intruder_alert.xml b/packages/SystemUI/res/layout/heads_up.xml index c4141ae9a5ae..b7c1666fd781 100644 --- a/packages/SystemUI/res/layout/intruder_alert.xml +++ b/packages/SystemUI/res/layout/heads_up.xml @@ -19,7 +19,7 @@ --> <!-- android:background="@drawable/status_bar_closed_default_background" --> -<com.android.systemui.statusbar.policy.IntruderAlertView +<com.android.systemui.statusbar.policy.HeadsUpNotificationView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -29,12 +29,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:id="@+id/contentHolder" - android:background="@drawable/intruder_window_bg" + android:background="@drawable/heads_up_window_bg" /> -<!-- <ImageView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:src="@drawable/title_bar_shadow" - android:scaleType="fitXY" - /> --> -</com.android.systemui.statusbar.policy.IntruderAlertView> +</com.android.systemui.statusbar.policy.HeadsUpNotificationView> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index ca6e06df232a..4a7d09067acb 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -27,7 +27,7 @@ <color name="notification_list_shadow_top">#80000000</color> <drawable name="recents_callout_line">#99ffffff</drawable> <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable> - <drawable name="intruder_bg_pressed">#ff33B5E5</drawable> + <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable> <drawable name="notification_header_bg">#FF000000</drawable> <color name="notification_panel_scrim_color">#B0000000</color> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7ddf261416bf..11940a3e3d58 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -34,7 +34,7 @@ <item name="android:wallpaperIntraOpenExitAnimation">@anim/wallpaper_recents_launch_from_launcher_exit</item> </style> - <style name="TextAppearance.StatusBar.IntruderAlert" + <style name="TextAppearance.StatusBar.HeadsUp" parent="@*android:style/TextAppearance.StatusBar"> </style> @@ -154,9 +154,9 @@ <style name="Animation.StatusBar"> </style> - <style name="Animation.StatusBar.IntruderAlert"> - <item name="android:windowEnterAnimation">@anim/priority_alert_enter</item> - <item name="android:windowExitAnimation">@anim/priority_alert_exit</item> + <style name="Animation.StatusBar.HeadsUp"> + <item name="android:windowEnterAnimation">@anim/heads_up_enter</item> + <item name="android:windowExitAnimation">@anim/heads_up_exit</item> </style> <style name="TextAppearance.StatusBar.PhoneTicker" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 3583081e4651..69fc3cff7b08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -35,10 +35,13 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; +import android.service.dreams.DreamService; +import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; @@ -83,16 +86,19 @@ public abstract class BaseStatusBar extends SystemUI implements protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; protected static final int MSG_OPEN_SEARCH_PANEL = 1024; protected static final int MSG_CLOSE_SEARCH_PANEL = 1025; - protected static final int MSG_SHOW_INTRUDER = 1026; - protected static final int MSG_HIDE_INTRUDER = 1027; + protected static final int MSG_SHOW_HEADS_UP = 1026; + protected static final int MSG_HIDE_HEADS_UP = 1027; - protected static final boolean ENABLE_INTRUDERS = false; + protected static final boolean ENABLE_HEADS_UP = true; + // scores above this threshold should be displayed in heads up mode. + private static final int INTERRUPTION_THRESHOLD = 10; // Should match the value in PhoneWindowManager public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; public static final int EXPANDED_LEAVE_ALONE = -10000; public static final int EXPANDED_FULL_OPEN = -10001; + private static final String SETTING_HEADS_UP = "heads_up_enabled"; protected CommandQueue mCommandQueue; protected IStatusBarService mBarService; @@ -102,7 +108,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected NotificationData mNotificationData = new NotificationData(); protected NotificationRowLayout mPile; - protected StatusBarNotification mCurrentlyIntrudingNotification; + protected StatusBarNotification mCurrentlyInterruptingNotification; // used to notify status bar for suppressing notification LED protected boolean mPanelSlightlyVisible; @@ -116,6 +122,11 @@ public abstract class BaseStatusBar extends SystemUI implements protected int mLayoutDirection; private Locale mLocale; + protected boolean mUseHeadsUp = true; + + protected IDreamManager mDreamManager; + KeyguardManager mKeyguardManager; + PowerManager mPowerManager; // UI-specific methods @@ -155,6 +166,19 @@ public abstract class BaseStatusBar extends SystemUI implements } }; + final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + mUseHeadsUp = ENABLE_HEADS_UP && 0 != Settings.Global.getInt( + mContext.getContentResolver(), SETTING_HEADS_UP, 0); + Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); + if (!mUseHeadsUp) { + Log.d(TAG, "dismissing any existing heads up notification on disable event"); + mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); + } + } + }; + private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { @Override public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) { @@ -204,11 +228,21 @@ public abstract class BaseStatusBar extends SystemUI implements mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); mDisplay = mWindowManager.getDefaultDisplay(); + mDreamManager = IDreamManager.Stub.asInterface( + ServiceManager.checkService(DreamService.DREAM_SERVICE)); + mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mProvisioningObserver.onChange(false); // set up mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true, mProvisioningObserver); + mHeadsUpObserver.onChange(false); // set up + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(SETTING_HEADS_UP), true, + mHeadsUpObserver); + mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -398,7 +432,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } - public void dismissIntruder() { + public void dismissHeadsUp() { // pass } @@ -677,13 +711,13 @@ public abstract class BaseStatusBar extends SystemUI implements return new NotificationClicker(intent, pkg, tag, id); } - private class NotificationClicker implements View.OnClickListener { + protected class NotificationClicker implements View.OnClickListener { private PendingIntent mIntent; private String mPkg; private String mTag; private int mId; - NotificationClicker(PendingIntent intent, String pkg, String tag, int id) { + public NotificationClicker(PendingIntent intent, String pkg, String tag, int id) { mIntent = intent; mPkg = pkg; mTag = tag; @@ -730,9 +764,6 @@ public abstract class BaseStatusBar extends SystemUI implements // close the shade if it was open animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); visibilityChanged(false); - - // If this click was on the intruder alert, hide that instead -// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); } } /** @@ -997,18 +1028,30 @@ public abstract class BaseStatusBar extends SystemUI implements setAreThereNotifications(); updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - // See if we need to update the intruder. - if (ENABLE_INTRUDERS && oldNotification == mCurrentlyIntrudingNotification) { - if (DEBUG) Log.d(TAG, "updating the current intruder:" + notification); + // See if we need to update the heads up. + if (ENABLE_HEADS_UP && oldNotification == mCurrentlyInterruptingNotification) { + if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification); // XXX: this is a hack for Alarms. The real implementation will need to *update* - // the intruder. - if (notification.getNotification().fullScreenIntent == null) { // TODO(dsandler): consistent logic with add() - if (DEBUG) Log.d(TAG, "no longer intrudes!"); - mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); + // the heads up. + if (!shouldInterrupt(notification)) { + if (DEBUG) Log.d(TAG, "no longer interrupts!"); + mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); } } } + protected boolean shouldInterrupt(StatusBarNotification notification) { + boolean interrupt = notification.getNotification().fullScreenIntent == null + && notification.getScore() >= INTERRUPTION_THRESHOLD + && mPowerManager.isScreenOn() && !mKeyguardManager.isKeyguardLocked(); + try { + interrupt = interrupt && !mDreamManager.isDreaming(); + } catch (RemoteException e) { + Log.d(TAG, "failed to query dream manager", e); + } + return interrupt; + } + // Q: What kinds of notifications should show during setup? // A: Almost none! Only things coming from the system (package is "android") that also // have special "kind" tags marking them as relevant for setup (see below). diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index ee94cc830925..fd99f5b1b178 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -45,12 +45,9 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; -import android.service.dreams.DreamService; -import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; import android.util.DisplayMetrics; import android.util.EventLog; @@ -76,7 +73,6 @@ import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; - import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.EventLogTags; import com.android.systemui.R; @@ -90,7 +86,7 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.DateView; -import com.android.systemui.statusbar.policy.IntruderAlertView; +import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NotificationRowLayout; @@ -127,7 +123,7 @@ public class PhoneStatusBar extends BaseStatusBar { // 1020-1030 reserved for BaseStatusBar // will likely move to a resource or other tunable param at some point - private static final int INTRUDER_ALERT_DECAY_MS = 0; // disabled, was 10000; + private static final int HEADS_UP_DECAY_MS = 0; // disabled, was 10000; private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; @@ -169,8 +165,6 @@ public class PhoneStatusBar extends BaseStatusBar { Display mDisplay; Point mCurrentDisplaySize = new Point(); - IDreamManager mDreamManager; - StatusBarWindowView mStatusBarWindow; PhoneStatusBarView mStatusBarView; @@ -230,8 +224,8 @@ public class PhoneStatusBar extends BaseStatusBar { // the date view DateView mDateView; - // for immersive activities - private IntruderAlertView mIntruderAlertView; + // for heads up notifications + private HeadsUpNotificationView mHeadsUpNotificationView; // on-screen navigation buttons private NavigationBarView mNavigationBarView = null; @@ -358,14 +352,11 @@ public class PhoneStatusBar extends BaseStatusBar { .getDefaultDisplay(); mDisplay.getSize(mCurrentDisplaySize); - mDreamManager = IDreamManager.Stub.asInterface( - ServiceManager.checkService(DreamService.DREAM_SERVICE)); - super.start(); // calls createAndAddWindows() addNavigationBar(); - if (ENABLE_INTRUDERS) addIntruderView(); + if (ENABLE_HEADS_UP) addHeadsUpView(); // Lastly, call to the icon policy to install/update all the icons. mIconPolicy = new PhoneStatusBarPolicy(mContext); @@ -424,10 +415,11 @@ public class PhoneStatusBar extends BaseStatusBar { mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor( R.color.notification_panel_solid_background))); } - if (ENABLE_INTRUDERS) { - mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null); - mIntruderAlertView.setVisibility(View.GONE); - mIntruderAlertView.setBar(this); + if (ENABLE_HEADS_UP) { + mHeadsUpNotificationView = + (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null); + mHeadsUpNotificationView.setVisibility(View.GONE); + mHeadsUpNotificationView.setBar(this); } if (MULTIUSER_DEBUG) { mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info); @@ -833,7 +825,7 @@ public class PhoneStatusBar extends BaseStatusBar { return lp; } - private void addIntruderView() { + private void addHeadsUpView() { WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, @@ -847,11 +839,11 @@ public class PhoneStatusBar extends BaseStatusBar { PixelFormat.TRANSLUCENT); lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; //lp.y += height * 1.5; // FIXME - lp.setTitle("IntruderAlert"); + lp.setTitle("Heads Up"); lp.packageName = mContext.getPackageName(); - lp.windowAnimations = R.style.Animation_StatusBar_IntruderAlert; + lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp; - mWindowManager.addView(mIntruderAlertView, lp); + mWindowManager.addView(mHeadsUpNotificationView, lp); } public void refreshAllStatusBarIcons() { @@ -895,52 +887,31 @@ public class PhoneStatusBar extends BaseStatusBar { StatusBarIconView iconView = addNotificationViews(key, notification); if (iconView == null) return; - boolean immersive = false; - try { - immersive = ActivityManagerNative.getDefault().isTopActivityImmersive(); - if (DEBUG) { - Log.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive")); - } - } catch (RemoteException ex) { - } - - /* - * DISABLED due to missing API - if (ENABLE_INTRUDERS && ( - // TODO(dsandler): Only if the screen is on - notification.notification.intruderView != null)) { - Log.d(TAG, "Presenting high-priority notification"); - // special new transient ticker mode - // 1. Populate mIntruderAlertView - - if (notification.notification.intruderView == null) { - Log.e(TAG, notification.notification.toString() + " wanted to intrude but intruderView was null"); - return; - } + if (mUseHeadsUp && shouldInterrupt(notification)) { + if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); + // 1. Populate mHeadsUpNotificationView // bind the click event to the content area - PendingIntent contentIntent = notification.notification.contentIntent; + PendingIntent contentIntent = notification.getNotification().contentIntent; final View.OnClickListener listener = (contentIntent != null) ? new NotificationClicker(contentIntent, - notification.pkg, notification.tag, notification.id) + notification.getPackageName(), notification.getTag(), notification.getId()) : null; - mIntruderAlertView.applyIntruderContent(notification.notification.intruderView, listener); + if (mHeadsUpNotificationView.applyContent(notification.getNotification(), listener)) { - mCurrentlyIntrudingNotification = notification; + mCurrentlyInterruptingNotification = notification; - // 2. Animate mIntruderAlertView in - mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER); + // 2. Animate mHeadsUpNotificationView in + mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); - // 3. Set alarm to age the notification off (TODO) - mHandler.removeMessages(MSG_HIDE_INTRUDER); - if (INTRUDER_ALERT_DECAY_MS > 0) { - mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS); + // 3. Set alarm to age the notification off (TODO) + mHandler.removeMessages(MSG_HIDE_HEADS_UP); + if (HEADS_UP_DECAY_MS > 0) { + mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, HEADS_UP_DECAY_MS); + } } - } else - */ - - if (notification.getNotification().fullScreenIntent != null) { + } else if (notification.getNotification().fullScreenIntent != null) { // Stop screensaver if the notification has a full-screen intent. // (like an incoming phone call) awakenDreams(); @@ -954,8 +925,8 @@ public class PhoneStatusBar extends BaseStatusBar { } else { // usual case: status bar visible & not immersive - // show the ticker if there isn't an intruder too - if (mCurrentlyIntrudingNotification == null) { + // show the ticker if there isn't already a heads up + if (mCurrentlyInterruptingNotification == null) { tick(null, notification, true); } } @@ -976,8 +947,8 @@ public class PhoneStatusBar extends BaseStatusBar { // Recalculate the position of the sliding windows and the titles. updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) { - mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); + if (ENABLE_HEADS_UP && old == mCurrentlyInterruptingNotification) { + mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); } if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0) { @@ -1359,12 +1330,12 @@ public class PhoneStatusBar extends BaseStatusBar { case MSG_CLOSE_PANELS: animateCollapsePanels(); break; - case MSG_SHOW_INTRUDER: - setIntruderAlertVisibility(true); + case MSG_SHOW_HEADS_UP: + setHeadsUpVisibility(true); break; - case MSG_HIDE_INTRUDER: - setIntruderAlertVisibility(false); - mCurrentlyIntrudingNotification = null; + case MSG_HIDE_HEADS_UP: + setHeadsUpVisibility(false); + mCurrentlyInterruptingNotification = null; break; } } @@ -2487,22 +2458,20 @@ public class PhoneStatusBar extends BaseStatusBar { mCurrentUserId); } - private void setIntruderAlertVisibility(boolean vis) { - if (!ENABLE_INTRUDERS) return; - if (DEBUG) { - Log.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window"); - } - mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE); + private void setHeadsUpVisibility(boolean vis) { + if (!ENABLE_HEADS_UP) return; + if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); + mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); } - public void dismissIntruder() { - if (mCurrentlyIntrudingNotification == null) return; + public void dismissHeadsUp() { + if (mCurrentlyInterruptingNotification == null) return; try { mBarService.onNotificationClear( - mCurrentlyIntrudingNotification.getPackageName(), - mCurrentlyIntrudingNotification.getTag(), - mCurrentlyIntrudingNotification.getId()); + mCurrentlyInterruptingNotification.getPackageName(), + mCurrentlyInterruptingNotification.getTag(), + mCurrentlyInterruptingNotification.getId()); } catch (android.os.RemoteException ex) { // oh well } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IntruderAlertView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java index dbd36affcfd9..dbf9957461c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IntruderAlertView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy; +import android.app.Notification; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -27,14 +28,13 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.RemoteViews; import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.statusbar.BaseStatusBar; -public class IntruderAlertView extends LinearLayout implements SwipeHelper.Callback { - private static final String TAG = "IntruderAlertView"; +public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper.Callback { + private static final String TAG = "HeadsUpNotificationView"; private static final boolean DEBUG = false; Rect mTmpRect = new Rect(); @@ -44,14 +44,14 @@ public class IntruderAlertView extends LinearLayout implements SwipeHelper.Callb BaseStatusBar mBar; private ViewGroup mContentHolder; - private RemoteViews mIntruderRemoteViews; + private Notification mHeadsUp; private OnClickListener mOnClickListener; - public IntruderAlertView(Context context, AttributeSet attrs) { + public HeadsUpNotificationView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public IntruderAlertView(Context context, AttributeSet attrs, int defStyle) { + public HeadsUpNotificationView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setOrientation(LinearLayout.VERTICAL); @@ -64,9 +64,9 @@ public class IntruderAlertView extends LinearLayout implements SwipeHelper.Callb mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop); mContentHolder = (ViewGroup) findViewById(R.id.contentHolder); - if (mIntruderRemoteViews != null) { + if (mHeadsUp != null) { // whoops, we're on already! - applyIntruderContent(mIntruderRemoteViews, mOnClickListener); + applyContent(mHeadsUp, mOnClickListener); } } @@ -92,8 +92,8 @@ public class IntruderAlertView extends LinearLayout implements SwipeHelper.Callb } public void onChildDismissed(View v) { - Log.v(TAG, "User swiped intruder to dismiss"); - mBar.dismissIntruder(); + Log.v(TAG, "User swiped heads up to dismiss"); + mBar.dismissHeadsUp(); } public void onBeginDrag(View v) { @@ -134,33 +134,34 @@ public class IntruderAlertView extends LinearLayout implements SwipeHelper.Callb } } - public void applyIntruderContent(RemoteViews intruderView, OnClickListener listener) { - if (DEBUG) { - Log.v(TAG, "applyIntruderContent: view=" + intruderView + " listener=" + listener); - } - mIntruderRemoteViews = intruderView; + public boolean applyContent(Notification headsUp, OnClickListener listener) { + mHeadsUp = headsUp; mOnClickListener = listener; - if (mContentHolder == null) { + if (mContentHolder == null) { // too soon! - return; + return false; + } + if (headsUp.contentView == null) { + // bad data + return false; } mContentHolder.setX(0); mContentHolder.setVisibility(View.VISIBLE); mContentHolder.setAlpha(1f); mContentHolder.removeAllViews(); - final View content = intruderView.apply(getContext(), mContentHolder); + final View content = headsUp.contentView.apply(getContext(), mContentHolder); if (listener != null) { content.setOnClickListener(listener); - - //content.setBackgroundResource(R.drawable.intruder_row_bg); - Drawable bg = getResources().getDrawable(R.drawable.intruder_row_bg); + + Drawable bg = getResources().getDrawable(R.drawable.heads_up_notification_row_bg); if (bg == null) { - Log.e(TAG, String.format("Can't find background drawable id=0x%08x", R.drawable.intruder_row_bg)); + Log.e(TAG, String.format("Can't find background drawable id=0x%08x", + R.drawable.heads_up_notification_row_bg)); } else { content.setBackgroundDrawable(bg); } } mContentHolder.addView(content); - + return true; } }
\ No newline at end of file diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 6e24d68301e7..1c1b0020b462 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1511,23 +1511,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { // Used to load label - final PackageManager pm = mContext.getPackageManager(); final CharSequence title = mRes.getText( com.android.internal.R.string.select_input_method); - final CharSequence imiLabel = imi.loadLabel(pm); - final CharSequence summary = mCurrentSubtype != null - ? TextUtils.concat(mCurrentSubtype.getDisplayName(mContext, - imi.getPackageName(), imi.getServiceInfo().applicationInfo), - (TextUtils.isEmpty(imiLabel) ? - "" : " - " + imiLabel)) - : imiLabel; + final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName( + mContext, imi, mCurrentSubtype); mImeSwitcherNotification.setLatestEventInfo( mContext, title, summary, mImeSwitchPendingIntent); if (mNotificationManager != null) { if (DEBUG) { - Slog.d(TAG, "--- show notification: label = " + imiLabel - + ", summary = " + summary); + Slog.d(TAG, "--- show notification: label = " + summary); } mNotificationManager.notifyAsUser(null, com.android.internal.R.string.select_input_method, diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 231cfe14c0c8..c6c9845979ad 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 e41f6424be8c..f99f4baa540c 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1782,7 +1782,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mActivityManagerService.dumpProcessTracker(fd, pw, args); + mActivityManagerService.mProcessTracker.dump(fd, pw, args); } } @@ -1808,7 +1808,7 @@ public final class ActivityManagerService extends ActivityManagerNative : mBatteryStatsService.getActiveStatistics().getIsOnBattery(); mBatteryStatsService.getActiveStatistics().setCallback(this); - mProcessTracker = new ProcessTracker(new File(systemDir, "procstats")); + mProcessTracker = new ProcessTracker(this, new File(systemDir, "procstats")); mUsageStatsService = new UsageStatsService(new File(systemDir, "usagestats").toString()); mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml")); @@ -11371,12 +11371,6 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } - final void dumpProcessTracker(FileDescriptor fd, PrintWriter pw, String[] args) { - synchronized (this) { - mProcessTracker.dumpLocked(fd, pw, args); - } - } - private final boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr, boolean always) { final boolean inLaunching = mLaunchingProviders.contains(cpr); diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 0e7513c926a9..5238bd18f5ba 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/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 1671d24b0d8e..14f410206a6b 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -425,8 +425,14 @@ final class ProcessRecord { } else { sb.append('u'); sb.append(userId); - sb.append('a'); - sb.append(UserHandle.getAppId(info.uid)); + int appId = UserHandle.getAppId(info.uid); + if (appId >= Process.FIRST_APPLICATION_UID) { + sb.append('a'); + sb.append(appId - Process.FIRST_APPLICATION_UID); + } else { + sb.append('s'); + sb.append(appId); + } if (uid != info.uid) { sb.append('i'); sb.append(UserHandle.getAppId(uid) - Process.FIRST_ISOLATED_UID); diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java index 488582d4135f..0ea93e828a92 100644 --- a/services/java/com/android/server/am/ProcessTracker.java +++ b/services/java/com/android/server/am/ProcessTracker.java @@ -140,9 +140,12 @@ public final class ProcessTracker { static final int MAX_HISTORIC_STATES = 4; // Maximum number of historic states we will keep. static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames. + static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. + static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. static long COMMIT_PERIOD = 24*60*60*1000; // Commit current stats every day. + final Object mLock; final File mBaseDir; State mState; boolean mCommitPending; @@ -694,7 +697,7 @@ public final class ProcessTracker { mTimePeriodStartClock).toString(); if (mBaseDir != null) { mFile = new AtomicFile(new File(mBaseDir, - STATE_FILE_PREFIX + mTimePeriodStartClockStr)); + STATE_FILE_PREFIX + mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); } } @@ -1255,8 +1258,7 @@ public final class ProcessTracker { return ps; } - void dumpLocked(PrintWriter pw, String reqPackage, boolean dumpAll) { - final long now = SystemClock.uptimeMillis(); + void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) { ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); boolean printedHeader = false; for (int ip=0; ip<pkgMap.size(); ip++) { @@ -1318,29 +1320,53 @@ public final class ProcessTracker { } } - dumpFilteredProcesses(pw, "Processes running while critical mem:", " ", - new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - new int[] {ADJ_MEM_FACTOR_CRITICAL}, - new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS}, - now, reqPackage); - dumpFilteredProcesses(pw, "Processes running while low mem:", " ", - new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - new int[] {ADJ_MEM_FACTOR_LOW}, - new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS}, - now, reqPackage); - dumpFilteredProcesses(pw, "Processes running while moderate mem:", " ", - new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - new int[] {ADJ_MEM_FACTOR_MODERATE}, - new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS}, - now, reqPackage); - dumpFilteredProcesses(pw, "Processes running while normal mem:", " ", - new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - new int[] {ADJ_MEM_FACTOR_NORMAL}, - new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS}, + if (reqPackage == null) { + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + printedHeader = false; + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + ProcessState proc = uids.valueAt(iu); + if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING + && proc.mPssTableSize == 0) { + continue; + } + if (!printedHeader) { + pw.println("Process Stats:"); + printedHeader = true; + } + pw.print(" * "); pw.print(procName); pw.print(" / "); + UserHandle.formatUid(pw, uid); + pw.print(" ("); pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); pw.println(":"); + dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); + } + } + + pw.println(); + pw.println("Summary:"); + dumpSummaryLocked(pw, reqPackage, now); + } + + if (dumpAll) { + pw.println(); + pw.println("Internal state:"); + pw.print(" mFile="); pw.println(mFile.getBaseFile()); + pw.print(" Num long arrays: "); pw.println(mLongs.size()); + pw.print(" Next long entry: "); pw.println(mNextLong); + pw.print(" mRunning="); pw.println(mRunning); + } + } + + void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) { + dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + new int[] { STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS }, now, reqPackage); pw.println(); pw.println("Run time Stats:"); @@ -1352,26 +1378,20 @@ public final class ProcessTracker { pw.print(" Total elapsed time: "); TimeUtils.formatDuration( (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) - - mTimePeriodStartRealtime, pw); + - mTimePeriodStartRealtime, pw); pw.println(); - if (dumpAll) { - pw.println(); - pw.println("Internal state:"); - pw.print(" mFile="); pw.println(mFile.getBaseFile()); - pw.print(" Num long arrays: "); pw.println(mLongs.size()); - pw.print(" Next long entry: "); pw.println(mNextLong); - pw.print(" mRunning="); pw.println(mRunning); - } } - void dumpFilteredProcesses(PrintWriter pw, String header, String prefix, + void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, int[] screenStates, int[] memStates, int[] procStates, long now, String reqPackage) { ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates, procStates, now, reqPackage); if (procs.size() > 0) { - pw.println(); - pw.println(header); - dumpProcessList(pw, prefix, procs, screenStates, memStates, procStates, now); + if (header != null) { + pw.println(); + pw.println(header); + } + dumpProcessSummary(pw, prefix, procs, screenStates, memStates, procStates, now); } } @@ -1542,7 +1562,8 @@ public final class ProcessTracker { } } - public ProcessTracker(File file) { + public ProcessTracker(Object lock, File file) { + mLock = lock; mBaseDir = file; mBaseDir.mkdirs(); mState = new State(mBaseDir, this); @@ -1637,27 +1658,34 @@ public final class ProcessTracker { mState.writeStateLocked(sync, commitPending); } - private ArrayList<String> getCommittedFiles(int minNum) { + private ArrayList<String> getCommittedFiles(int minNum, boolean inclAll) { File[] files = mBaseDir.listFiles(); if (files == null || files.length <= minNum) { return null; } ArrayList<String> filesArray = new ArrayList<String>(files.length); - String currentFile = mState.mFile.getBaseFile().toString(); + String currentFile = mState.mFile.getBaseFile().getPath(); if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile); for (int i=0; i<files.length; i++) { File file = files[i]; - if (DEBUG) Slog.d(TAG, "Collecting: " + file); - if (!file.toString().equals(currentFile)) { - filesArray.add(files[i].toString()); + String fileStr = file.getPath(); + if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr); + if (!inclAll && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) { + if (DEBUG) Slog.d(TAG, "Skipping: already checked in"); + continue; + } + if (fileStr.equals(currentFile)) { + if (DEBUG) Slog.d(TAG, "Skipping: current stats"); + continue; } + filesArray.add(fileStr); } Collections.sort(filesArray); return filesArray; } public void trimHistoricStatesWriteLocked() { - ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES); + ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, true); if (filesArray == null) { return; } @@ -1803,6 +1831,76 @@ public final class ProcessTracker { pw.println(); } + static final class ProcessDataCollection { + final int[] screenStates; + final int[] memStates; + final int[] procStates; + + long totalTime; + long numPss; + long minPss; + long avgPss; + long maxPss; + + ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { + screenStates = _screenStates; + memStates = _memStates; + procStates = _procStates; + } + + void print(PrintWriter pw, boolean full) { + TimeUtils.formatDuration(totalTime, pw); + if (numPss > 0) { + pw.print(" ("); + printSizeValue(pw, minPss * 1024); + pw.print("-"); + printSizeValue(pw, avgPss * 1024); + pw.print("-"); + printSizeValue(pw, maxPss * 1024); + if (full) { + pw.print(" over "); + pw.print(numPss); + } + pw.print(")"); + } + } + } + + static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) { + data.totalTime = 0; + data.numPss = data.minPss = data.avgPss = data.maxPss = 0; + for (int is=0; is<data.screenStates.length; is++) { + for (int im=0; im<data.memStates.length; im++) { + for (int ip=0; ip<data.procStates.length; ip++) { + int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT) + + data.procStates[ip]; + data.totalTime += proc.getDuration(bucket, now); + long samples = proc.getPssSampleCount(bucket); + if (samples > 0) { + long minPss = proc.getPssMinimum(bucket); + long avgPss = proc.getPssAverage(bucket); + long maxPss = proc.getPssMaximum(bucket); + if (data.numPss == 0) { + data.minPss = minPss; + data.avgPss = avgPss; + data.maxPss = maxPss; + } else { + if (minPss < data.minPss) { + data.minPss = minPss; + } + data.avgPss = (long)( ((data.avgPss*(double)data.numPss) + + (avgPss*(double)samples)) / (data.numPss+samples) ); + if (maxPss > data.maxPss) { + data.maxPss = maxPss; + } + } + data.numPss += samples; + } + } + } + } + } + static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, int[] procStates, long now) { long totalTime = 0; @@ -1818,7 +1916,7 @@ public final class ProcessTracker { for (int is=0; is<screenStates.length; is++) { for (int im=0; im<memStates.length; im++) { for (int ip=0; ip<procStates.length; ip++) { - int bucket = ((screenStates[is]+ memStates[im]) * STATE_COUNT) + int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT) + procStates[ip]; totalTime += proc.getDuration(bucket, now); } @@ -1910,12 +2008,12 @@ public final class ProcessTracker { pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); pw.print(count); pw.print(" samples "); - pw.print(proc.getPssMinimum(bucket)); - pw.print("kB "); - pw.print(proc.getPssAverage(bucket)); - pw.print("kB "); - pw.print(proc.getPssMaximum(bucket)); - pw.println("kB"); + printSizeValue(pw, proc.getPssMinimum(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssAverage(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssMaximum(bucket) * 1024); + pw.println(); } } } @@ -2017,6 +2115,94 @@ public final class ProcessTracker { } } + static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix, + String label, int[] screenStates, int[] memStates, int[] procStates, + long now, boolean full) { + ProcessDataCollection totals = new ProcessDataCollection(screenStates, + memStates, procStates); + computeProcessData(proc, totals, now); + if (totals.totalTime != 0 || totals.numPss != 0) { + if (prefix != null) { + pw.print(prefix); + } + if (label != null) { + pw.print(label); + } + totals.print(pw, full); + if (prefix != null) { + pw.println(); + } + } + } + + static void dumpProcessSummary(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, + int[] screenStates, int[] memStates, int[] procStates, long now) { + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(prefix); + pw.print("* "); + pw.print(proc.mName); + pw.print(" / "); + UserHandle.formatUid(pw, proc.mUid); + pw.println(":"); + dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates, + procStates, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates, + new int[] {STATE_PERSISTENT}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates, + new int[] {STATE_TOP}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Foreground: ", screenStates, memStates, + new int[] {STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates, + new int[] {STATE_BACKUP}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates, + new int[] {STATE_SERVICE}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates, + new int[] {STATE_HOME}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Previous: ", screenStates, memStates, + new int[] {STATE_PREVIOUS}, now, true); + dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates, + new int[] {STATE_CACHED}, now, true); + } + } + + private static void printSizeValue(PrintWriter pw, long number) { + float result = number; + String suffix = ""; + if (result > 900) { + suffix = "KB"; + result = result / 1024; + } + if (result > 900) { + suffix = "MB"; + result = result / 1024; + } + if (result > 900) { + suffix = "GB"; + result = result / 1024; + } + if (result > 900) { + suffix = "TB"; + result = result / 1024; + } + if (result > 900) { + suffix = "PB"; + result = result / 1024; + } + String value; + if (result < 1) { + value = String.format("%.2f", result); + } else if (result < 10) { + value = String.format("%.2f", result); + } else if (result < 100) { + value = String.format("%.2f", result); + } else { + value = String.format("%.0f", result); + } + pw.print(value); + pw.print(suffix); + } + static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now) { @@ -2171,7 +2357,7 @@ public final class ProcessTracker { static private void dumpHelp(PrintWriter pw) { pw.println("Process stats (procstats) dump options:"); pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]"); - pw.println(" [--include-committed] [--commit] [--write] [-h] [<package.name>]"); + pw.println(" [--details] [--current] [--commit] [--write] [-h] [<package.name>]"); pw.println(" --checkin: perform a checkin: print and delete old committed states."); pw.println(" --c: print only state in checkin format."); pw.println(" --csv: output data suitable for putting in a spreadsheet."); @@ -2179,7 +2365,8 @@ public final class ProcessTracker { pw.println(" --csv-mem: norm, mod, low, crit."); pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,"); pw.println(" service, home, prev, cached"); - pw.println(" --include-committed: also dump any old committed states."); + pw.println(" --details: dump all execution details, not just summary."); + pw.println(" --current: only dump current state."); pw.println(" --commit: commit current stats to disk and reset to start new stats."); pw.println(" --write: write current in-memory stats to disk."); pw.println(" --read: replace current stats with last-written stats."); @@ -2188,13 +2375,14 @@ public final class ProcessTracker { pw.println(" <package.name>: optional name of package to filter output by."); } - public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { final long now = SystemClock.uptimeMillis(); boolean isCheckin = false; boolean isCompact = false; boolean isCsv = false; - boolean includeCommitted = false; + boolean currentOnly = false; + boolean dumpDetails = false; boolean dumpAll = false; String reqPackage = null; boolean csvSepScreenStats = false; @@ -2264,8 +2452,10 @@ public final class ProcessTracker { return; } csvSepProcStats = sep[0]; - } else if ("--include-committed".equals(arg)) { - includeCommitted = true; + } else if ("--details".equals(arg)) { + dumpDetails = true; + } else if ("--current".equals(arg)) { + currentOnly = true; } else if ("--commit".equals(arg)) { mState.writeStateLocked(true, true); pw.println("Process stats committed."); @@ -2282,8 +2472,8 @@ public final class ProcessTracker { dumpHelp(pw); return; } else if ("-a".equals(arg)) { + dumpDetails = true; dumpAll = true; - includeCommitted = true; } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); dumpHelp(pw); @@ -2294,9 +2484,11 @@ public final class ProcessTracker { IPackageManager pm = AppGlobals.getPackageManager(); if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) { reqPackage = arg; - // We will automatically include all committed state, - // since we are going to end up with much less printed. - includeCommitted = true; + // Include all details, since we know we are only going to + // be dumping a smaller set of data. In fact only the details + // container per-package data, so that are needed to be able + // to dump anything at all when filtering by package. + dumpDetails = true; } } catch (RemoteException e) { } @@ -2330,70 +2522,94 @@ public final class ProcessTracker { } } pw.println(); - dumpFilteredProcessesCsvLocked(pw, null, - csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, - csvSepProcStats, csvProcStats, now, reqPackage); - /* - dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", - false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, - true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, - STATE_PREVIOUS, STATE_CACHED}, - now, reqPackage); - dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", - false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, - false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, - ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, - true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, - STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, - STATE_PREVIOUS, STATE_CACHED}, - now, reqPackage); - */ + synchronized (mLock) { + dumpFilteredProcessesCsvLocked(pw, null, + csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, + csvSepProcStats, csvProcStats, now, reqPackage); + /* + dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", + false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, + true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, + true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, + STATE_PREVIOUS, STATE_CACHED}, + now, reqPackage); + dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", + false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, + false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, + ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, + true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, + STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, + STATE_PREVIOUS, STATE_CACHED}, + now, reqPackage); + */ + } return; } boolean sepNeeded = false; - if (includeCommitted || isCheckin) { - ArrayList<String> files = getCommittedFiles(0); - if (files != null) { - for (int i=0; i<files.size(); i++) { - if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); - try { - State state = new State(files.get(i)); - if (isCheckin || isCompact) { - state.dumpCheckinLocked(pw, reqPackage); - } else { - if (sepNeeded) { - pw.println(); + if (!currentOnly || isCheckin) { + mWriteLock.lock(); + try { + ArrayList<String> files = getCommittedFiles(0, !isCheckin); + if (files != null) { + for (int i=0; i<files.size(); i++) { + if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); + try { + State state = new State(files.get(i)); + String fileStr = state.mFile.getBaseFile().getPath(); + boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); + if (isCheckin || isCompact) { + // Don't really need to lock because we uniquely own this object. + state.dumpCheckinLocked(pw, reqPackage); } else { - sepNeeded = true; + if (sepNeeded) { + pw.println(); + } else { + sepNeeded = true; + } + pw.print("COMMITTED STATS FROM "); + pw.print(state.mTimePeriodStartClockStr); + if (checkedIn) pw.print(" (checked in)"); + pw.println(":"); + // Don't really need to lock because we uniquely own this object. + if (dumpDetails) { + state.dumpLocked(pw, reqPackage, now, dumpAll); + } else { + state.dumpSummaryLocked(pw, reqPackage, now); + } } - pw.print("COMMITTED STATS FROM "); - pw.print(state.mTimePeriodStartClockStr); - pw.println(":"); - state.dumpLocked(pw, reqPackage, dumpAll); - } - if (isCheckin) { - state.mFile.delete(); + if (isCheckin) { + // Rename file suffix to mark that it has checked in. + state.mFile.getBaseFile().renameTo(new File( + fileStr + STATE_FILE_CHECKIN_SUFFIX)); + } + } catch (Throwable e) { + pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); + e.printStackTrace(pw); } - } catch (Throwable e) { - pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); - e.printStackTrace(pw); + if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); } - if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); } + } finally { + mWriteLock.unlock(); } } if (!isCheckin) { - if (isCompact) { - mState.dumpCheckinLocked(pw, reqPackage); - } else { - if (sepNeeded) { - pw.println(); - pw.println("CURRENT STATS:"); + synchronized (mLock) { + if (isCompact) { + mState.dumpCheckinLocked(pw, reqPackage); + } else { + if (sepNeeded) { + pw.println(); + pw.println("CURRENT STATS:"); + } + if (dumpDetails) { + mState.dumpLocked(pw, reqPackage, now, dumpAll); + } else { + mState.dumpSummaryLocked(pw, reqPackage, now); + } } - mState.dumpLocked(pw, reqPackage, dumpAll); } } } diff --git a/services/java/com/android/server/pm/KeySetManager.java b/services/java/com/android/server/pm/KeySetManager.java index 3480b195b3f7..93992c21f328 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/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index d86f2c75c08e..29012127b04e 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -822,6 +822,11 @@ public class UserManagerService extends IUserManager.Stub { pinState.failedAttempts = failedAttempts; pinState.lastAttemptTime = lastAttemptTime; } + // If this is not a restricted profile and there is no restrictions pin, clean up + // any restrictions files that might have been left behind. + if (!userInfo.isRestricted() && salt == 0) { + cleanAppRestrictions(id); + } return userInfo; } catch (IOException ioe) { @@ -873,6 +878,26 @@ public class UserManagerService extends IUserManager.Stub { } } + /** + * Removes all the restrictions files (res_<packagename>) for a given user. + * Does not do any permissions checking. + */ + private void cleanAppRestrictions(int userId) { + synchronized (mPackagesLock) { + File dir = Environment.getUserSystemDirectory(userId); + String[] files = dir.list(); + if (files == null) return; + for (String fileName : files) { + if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) { + File resFile = new File(dir, fileName); + if (resFile.exists()) { + resFile.delete(); + } + } + } + } + } + @Override public UserInfo createUser(String name, int flags) { checkManageUsersPermission("Only the system can create users"); diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml index d7838eb65401..1a9288ce1993 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 400dff026293..405ff65a34fd 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 542b55addba5..000000000000 --- 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 5483f6490948..9a399d044af6 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 000000000000..c46327a3e0a6 --- /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 000000000000..28e055f704fe --- /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/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 375a16071284..ae41c9ac6c86 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -388,6 +388,10 @@ public class WifiNative { return doStringCommand("PKTCNT_POLL"); } + public void bssFlush() { + doBooleanCommand("BSS_FLUSH"); + } + public boolean startWpsPbc(String bssid) { if (TextUtils.isEmpty(bssid)) { return doBooleanCommand("WPS_PBC"); diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 1f51b2a622f7..d509b371ceec 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -2497,6 +2497,8 @@ public class WifiStateMachine extends StateMachine { if (DBG) log("set frequency band " + band); if (mWifiNative.setBand(band)) { mFrequencyBand.set(band); + // flush old data - like scan results + mWifiNative.bssFlush(); //Fetch the latest scan results when frequency band is set startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP); } else { |