diff options
Diffstat (limited to 'services/java/com')
62 files changed, 2315 insertions, 972 deletions
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 9be7045..c18fe0e 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -17,34 +17,28 @@ package com.android.server; import android.app.ActivityManagerNative; -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.widget.RemoteViews; import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetService; -import com.android.internal.widget.IRemoteViewsAdapterConnection; +import com.android.internal.util.IndentingPrintWriter; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -56,85 +50,11 @@ class AppWidgetService extends IAppWidgetService.Stub { private static final String TAG = "AppWidgetService"; - /* - * When identifying a Host or Provider based on the calling process, use the uid field. - * When identifying a Host or Provider based on a package manager broadcast, use the - * package given. - */ - - static class Provider { - int uid; - AppWidgetProviderInfo info; - ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>(); - PendingIntent broadcast; - boolean zombie; // if we're in safe mode, don't prune this just because nobody references it - - int tag; // for use while saving state (the index) - } - - static class Host { - int uid; - int hostId; - String packageName; - ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>(); - IAppWidgetHost callbacks; - boolean zombie; // if we're in safe mode, don't prune this just because nobody references it - - int tag; // for use while saving state (the index) - } - - static class AppWidgetId { - int appWidgetId; - Provider provider; - RemoteViews views; - Host host; - } - - /** - * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. - * This needs to be a static inner class since a reference to the ServiceConnection is held - * globally and may lead us to leak AppWidgetService instances (if there were more than one). - */ - static class ServiceConnectionProxy implements ServiceConnection { - private final IBinder mConnectionCb; - - ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) { - mConnectionCb = connectionCb; - } - public void onServiceConnected(ComponentName name, IBinder service) { - final IRemoteViewsAdapterConnection cb = - IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb); - try { - cb.onServiceConnected(service); - } catch (Exception e) { - e.printStackTrace(); - } - } - public void onServiceDisconnected(ComponentName name) { - disconnect(); - } - public void disconnect() { - final IRemoteViewsAdapterConnection cb = - IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb); - try { - cb.onServiceDisconnected(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - Context mContext; Locale mLocale; PackageManager mPackageManager; - AlarmManager mAlarmManager; - ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>(); - int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1; - final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>(); - ArrayList<Host> mHosts = new ArrayList<Host>(); boolean mSafeMode; - private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices; AppWidgetService(Context context) { @@ -195,9 +115,16 @@ class AppWidgetService extends IAppWidgetService.Stub }, UserHandle.ALL, userFilter, null, null); } + /** + * This returns the user id of the caller, if the caller is not the system process, + * otherwise it assumes that the calls are from the lockscreen and hence are meant for the + * current user. TODO: Instead, have lockscreen make explicit calls with userId + */ private int getCallingOrCurrentUserId() { int callingUid = Binder.getCallingUid(); - if (callingUid == android.os.Process.myUid()) { + // Also check the PID because Settings (power control widget) also runs as System UID + if (callingUid == android.os.Process.myUid() + && Binder.getCallingPid() == android.os.Process.myPid()) { try { return ActivityManagerNative.getDefault().getCurrentUser().id; } catch (RemoteException re) { @@ -272,13 +199,16 @@ class AppWidgetService extends IAppWidgetService.Stub } public void onUserRemoved(int userId) { - AppWidgetServiceImpl impl = mAppWidgetServices.get(userId); if (userId < 1) return; - - if (impl == null) { - AppWidgetServiceImpl.getSettingsFile(userId).delete(); - } else { - impl.onUserRemoved(); + synchronized (mAppWidgetServices) { + AppWidgetServiceImpl impl = mAppWidgetServices.get(userId); + mAppWidgetServices.remove(userId); + + if (impl == null) { + AppWidgetServiceImpl.getSettingsFile(userId).delete(); + } else { + impl.onUserRemoved(); + } } } @@ -286,17 +216,23 @@ class AppWidgetService extends IAppWidgetService.Stub } private AppWidgetServiceImpl getImplForUser(int userId) { - AppWidgetServiceImpl service = mAppWidgetServices.get(userId); - if (service == null) { - Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user"); - // TODO: Verify that it's a valid user - service = new AppWidgetServiceImpl(mContext, userId); - service.systemReady(mSafeMode); - // Assume that BOOT_COMPLETED was received, as this is a non-primary user. + boolean sendInitial = false; + AppWidgetServiceImpl service; + synchronized (mAppWidgetServices) { + service = mAppWidgetServices.get(userId); + if (service == null) { + Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding"); + // TODO: Verify that it's a valid user + service = new AppWidgetServiceImpl(mContext, userId); + service.systemReady(mSafeMode); + // Assume that BOOT_COMPLETED was received, as this is a non-primary user. + mAppWidgetServices.append(userId, service); + sendInitial = true; + } + } + if (sendInitial) { service.sendInitialBroadcasts(); - mAppWidgetServices.append(userId, service); } - return service; } @@ -325,15 +261,6 @@ class AppWidgetService extends IAppWidgetService.Stub return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetOptions(appWidgetId); } - static int[] getAppWidgetIds(Provider p) { - int instancesSize = p.instances.size(); - int appWidgetIds[] = new int[instancesSize]; - for (int i=0; i<instancesSize; i++) { - appWidgetIds[i] = p.instances.get(i).appWidgetId; - } - return appWidgetIds; - } - @Override public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).getInstalledProviders(); @@ -378,9 +305,15 @@ class AppWidgetService extends IAppWidgetService.Stub @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { // Dump the state of all the app widget providers - for (int i = 0; i < mAppWidgetServices.size(); i++) { - AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i); - service.dump(fd, pw, args); + synchronized (mAppWidgetServices) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + for (int i = 0; i < mAppWidgetServices.size(); i++) { + pw.println("User: " + mAppWidgetServices.keyAt(i)); + ipw.increaseIndent(); + AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i); + service.dump(fd, ipw, args); + ipw.decreaseIndent(); + } } } diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index e77f8cf..41617c8 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -87,6 +87,8 @@ class AppWidgetServiceImpl { private static final String SETTINGS_FILENAME = "appwidgets.xml"; private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes + private static boolean DBG = false; + /* * When identifying a Host or Provider based on the calling process, use the uid field. When * identifying a Host or Provider based on a package manager broadcast, use the package given. @@ -208,7 +210,12 @@ class AppWidgetServiceImpl { } } + private void log(String msg) { + Slog.i(TAG, "u=" + mUserId + ": " + msg); + } + void onConfigurationChanged() { + if (DBG) log("Got onConfigurationChanged()"); Locale revised = Locale.getDefault(); if (revised == null || mLocale == null || !(revised.equals(mLocale))) { mLocale = revised; @@ -235,6 +242,7 @@ class AppWidgetServiceImpl { } void onBroadcastReceived(Intent intent) { + if (DBG) log("onBroadcast " + intent); final String action = intent.getAction(); boolean added = false; boolean changed = false; @@ -425,7 +433,8 @@ class AppWidgetServiceImpl { mAppWidgetIds.add(id); saveStateLocked(); - + if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId + + " id=" + appWidgetId); return appWidgetId; } } @@ -518,6 +527,7 @@ class AppWidgetServiceImpl { } void cancelBroadcasts(Provider p) { + if (DBG) log("cancelBroadcasts for " + p); if (p.broadcast != null) { mAlarmManager.cancel(p.broadcast); long token = Binder.clearCallingIdentity(); @@ -531,6 +541,8 @@ class AppWidgetServiceImpl { } private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) { + if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId + + " provider=" + provider); final long ident = Binder.clearCallingIdentity(); try { synchronized (mAppWidgetIds) { @@ -825,12 +837,14 @@ class AppWidgetServiceImpl { } public RemoteViews getAppWidgetViews(int appWidgetId) { + if (DBG) log("getAppWidgetViews id=" + appWidgetId); synchronized (mAppWidgetIds) { ensureStateLoadedLocked(); AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); if (id != null) { return cloneIfLocalBinder(id.views); } + if (DBG) log(" couldn't find appwidgetid"); return null; } } @@ -854,7 +868,7 @@ class AppWidgetServiceImpl { if (appWidgetIds == null) { return; } - + if (DBG) log("updateAppWidgetIds views: " + views); int bitmapMemoryUsage = 0; if (views != null) { bitmapMemoryUsage = views.estimateMemoryUsage(); @@ -1280,8 +1294,8 @@ class AppWidgetServiceImpl { intent.setComponent(p.info.provider); long token = Binder.clearCallingIdentity(); try { - p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent, - PendingIntent.FLAG_UPDATE_CURRENT); + p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent, + PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId)); } finally { Binder.restoreCallingIdentity(token); } @@ -1353,7 +1367,7 @@ class AppWidgetServiceImpl { p.uid = activityInfo.applicationInfo.uid; Resources res = mContext.getPackageManager() - .getResourcesForApplication(activityInfo.applicationInfo); + .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId); TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AppWidgetProviderInfo); @@ -1597,8 +1611,7 @@ class AppWidgetServiceImpl { final IPackageManager packageManager = AppGlobals.getPackageManager(); try { - packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, - UserHandle.getCallingUserId()); + packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId); } catch (RemoteException e) { String[] pkgs = mContext.getPackageManager() .currentToCanonicalPackageNames(new String[] { pkg }); diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index 40758d3..dbffa97 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -27,6 +27,7 @@ import android.content.pm.PackageManager; import android.os.BatteryManager; import android.os.Binder; import android.os.FileUtils; +import android.os.Handler; import android.os.IBinder; import android.os.DropBoxManager; import android.os.RemoteException; @@ -66,6 +67,14 @@ import java.io.PrintWriter; * <p>"temperature" - int, current battery temperature in tenths of * a degree Centigrade</p> * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> + * + * <p> + * The battery service may be called by the power manager while holding its locks so + * we take care to post all outcalls into the activity manager to a handler. + * + * FIXME: Ideally the power manager would perform all of its calls into the battery + * service asynchronously itself. + * </p> */ public final class BatteryService extends Binder { private static final String TAG = BatteryService.class.getSimpleName(); @@ -89,6 +98,7 @@ public final class BatteryService extends Binder { private final Context mContext; private final IBatteryStats mBatteryStats; + private final Handler mHandler; private final Object mLock = new Object(); @@ -137,6 +147,7 @@ public final class BatteryService extends Binder { public BatteryService(Context context, LightsService lights) { mContext = context; + mHandler = new Handler(true /*async*/); mLed = new Led(context, lights); mBatteryStats = BatteryStatsService.getService(); @@ -228,12 +239,18 @@ public final class BatteryService extends Binder { private void shutdownIfNoPowerLocked() { // shut down gracefully if our battery is critically low and we are not powered. // wait until the system has booted before attempting to display the shutdown dialog. - if (mBatteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY) - && ActivityManagerNative.isSystemReady()) { - Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); - intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + if (mBatteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (ActivityManagerNative.isSystemReady()) { + Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); + intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } + } + }); } } @@ -241,12 +258,18 @@ public final class BatteryService extends Binder { // shut down gracefully if temperature is too high (> 68.0C by default) // wait until the system has booted before attempting to display the // shutdown dialog. - if (mBatteryTemperature > mShutdownBatteryTemperature - && ActivityManagerNative.isSystemReady()) { - Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); - intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + if (mBatteryTemperature > mShutdownBatteryTemperature) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (ActivityManagerNative.isSystemReady()) { + Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); + intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } + } + }); } } @@ -373,25 +396,47 @@ public final class BatteryService extends Binder { // Separate broadcast is sent for power connected / not connected // since the standard intent will not wake any applications and some // applications may want to have smart behavior based on this. - Intent statusIntent = new Intent(); - statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); if (mPlugType != 0 && mLastPlugType == 0) { - statusIntent.setAction(Intent.ACTION_POWER_CONNECTED); - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + mHandler.post(new Runnable() { + @Override + public void run() { + Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + } + }); } else if (mPlugType == 0 && mLastPlugType != 0) { - statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED); - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + mHandler.post(new Runnable() { + @Override + public void run() { + Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + } + }); } if (sendBatteryLow) { mSentLowBatteryBroadcast = true; - statusIntent.setAction(Intent.ACTION_BATTERY_LOW); - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + mHandler.post(new Runnable() { + @Override + public void run() { + Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + } + }); } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) { mSentLowBatteryBroadcast = false; - statusIntent.setAction(Intent.ACTION_BATTERY_OKAY); - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + mHandler.post(new Runnable() { + @Override + public void run() { + Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY); + statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL); + } + }); } // Update the battery LED @@ -416,7 +461,7 @@ public final class BatteryService extends Binder { private void sendIntentLocked() { // Pack up the values and broadcast them to everyone - Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); + final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING); @@ -446,7 +491,12 @@ public final class BatteryService extends Binder { ", icon:" + icon + ", invalid charger:" + mInvalidCharger); } - ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL); + mHandler.post(new Runnable() { + @Override + public void run() { + ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL); + } + }); } private void logBatteryStatsLocked() { diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java index ce75e35..e7cd279 100755 --- a/services/java/com/android/server/BluetoothManagerService.java +++ b/services/java/com/android/server/BluetoothManagerService.java @@ -481,7 +481,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); Intent i = new Intent(IBluetooth.class.getName()); if (!mContext.bindService(i, mConnection, - Context.BIND_AUTO_CREATE)) { + Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName()); } @@ -717,7 +717,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); mConnection.setGetNameAddressOnly(false); Intent i = new Intent(IBluetooth.class.getName()); - if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE)) { + if (!mContext.bindService(i, mConnection, Context.BIND_AUTO_CREATE, + UserHandle.USER_CURRENT)) { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName()); } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 9607624..a5e26a8 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.os.Binder; +import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; @@ -401,6 +402,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("disable-camera".equals(tag)) { disableCamera = Boolean.parseBoolean( parser.getAttributeValue(null, "value")); + } else if ("disable-keyguard-features".equals(tag)) { + disabledKeyguardFeatures = Integer.parseInt( + parser.getAttributeValue(null, "value")); } else { Slog.w(TAG, "Unknown admin tag: " + tag); } @@ -457,6 +461,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(encryptionRequested); pw.print(prefix); pw.print("disableCamera="); pw.println(disableCamera); + pw.print(prefix); pw.print("disabledKeyguardFeatures="); + pw.println(disabledKeyguardFeatures); } } @@ -1827,7 +1833,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getIPowerManager().goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN); // Ensure the device is locked - getWindowManager().lockNow(); + getWindowManager().lockNow(null); } catch (RemoteException e) { } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 22fd508..ffbfef6 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -900,7 +900,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? " + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID + " calling userId = " + userId + ", foreground user id = " - + mSettings.getCurrentUserId() + ", calling uid = " + Binder.getCallingPid()); + + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()); } if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) { return true; @@ -2531,7 +2531,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!TextUtils.isEmpty(inputMethodId)) { intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId); } - mContext.startActivity(intent); + mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); } private void showConfigureInputMethods() { @@ -2539,7 +2539,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivity(intent); + mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); } private boolean isScreenLocked() { @@ -2673,6 +2673,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSwitchingDialog.setCanceledOnTouchOutside(true); mSwitchingDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); + mSwitchingDialog.getWindow().getAttributes().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method"); mSwitchingDialog.show(); } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index ae95c4c..c5016e6 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -226,7 +226,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run updateProvidersLocked(); } } - }); + }, UserHandle.USER_ALL); mPackageMonitor.register(mContext, Looper.myLooper(), true); // listen for user change @@ -289,7 +289,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run mContext, LocationManager.NETWORK_PROVIDER, NETWORK_LOCATION_SERVICE_ACTION, - providerPackageNames, mLocationHandler); + providerPackageNames, mLocationHandler, mCurrentUserId); if (networkProvider != null) { mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider); mProxyProviders.add(networkProvider); @@ -303,18 +303,20 @@ public class LocationManagerService extends ILocationManager.Stub implements Run mContext, LocationManager.FUSED_PROVIDER, FUSED_LOCATION_SERVICE_ACTION, - providerPackageNames, mLocationHandler); + providerPackageNames, mLocationHandler, mCurrentUserId); if (fusedLocationProvider != null) { addProviderLocked(fusedLocationProvider); mProxyProviders.add(fusedLocationProvider); mEnabledProviders.add(fusedLocationProvider.getName()); + mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider); } else { Slog.e(TAG, "no fused location provider found", new IllegalStateException("Location service needs a fused location provider")); } // bind to geocoder provider - mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames); + mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames, + mCurrentUserId); if (mGeocodeProvider == null) { Slog.e(TAG, "no geocoder provider found"); } @@ -325,11 +327,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run * @param userId the new active user's UserId */ private void switchUser(int userId) { - //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this + mBlacklist.switchUser(userId); synchronized (mLock) { - // TODO: inform previous user's Receivers that they will no longer receive updates + mLastLocation.clear(); + for (LocationProviderInterface p : mProviders) { + updateProviderListenersLocked(p.getName(), false, mCurrentUserId); + p.switchUser(userId); + } mCurrentUserId = userId; - // TODO: inform new user's Receivers that they are back on the update train + updateProvidersLocked(); } } @@ -586,7 +592,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } - private boolean isAllowedBySettingsLocked(String provider) { + private boolean isAllowedBySettingsLocked(String provider, int userId) { + if (userId != mCurrentUserId) { + return false; + } if (mEnabledProviders.contains(provider)) { return true; } @@ -596,7 +605,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Use system settings ContentResolver resolver = mContext.getContentResolver(); - return Settings.Secure.isLocationProviderEnabled(resolver, provider); + return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId); } /** @@ -639,7 +648,27 @@ public class LocationManagerService extends ILocationManager.Stub implements Run == PackageManager.PERMISSION_GRANTED) || (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED); + } else { + // mock providers + LocationProviderInterface lp = mMockProviders.get(provider); + if (lp != null) { + ProviderProperties properties = lp.getProperties(); + if (properties != null) { + if (properties.mRequiresSatellite) { + // provider requiring satellites require FINE permission + return mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED; + } else if (properties.mRequiresNetwork || properties.mRequiresCell) { + // provider requiring network and or cell require COARSE or FINE + return (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED) || + (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED); + } + } + } } + return false; } @@ -674,24 +703,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public List<String> getProviders(Criteria criteria, boolean enabledOnly) { ArrayList<String> out; - synchronized (mLock) { - out = new ArrayList<String>(mProviders.size()); - for (LocationProviderInterface provider : mProviders) { - String name = provider.getName(); - if (LocationManager.FUSED_PROVIDER.equals(name)) { - continue; - } - if (isAllowedProviderSafe(name)) { - if (enabledOnly && !isAllowedBySettingsLocked(name)) { + int callingUserId = UserHandle.getCallingUserId(); + long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + out = new ArrayList<String>(mProviders.size()); + for (LocationProviderInterface provider : mProviders) { + String name = provider.getName(); + if (LocationManager.FUSED_PROVIDER.equals(name)) { continue; } - if (criteria != null && !LocationProvider.propertiesMeetCriteria( - name, provider.getProperties(), criteria)) { - continue; + if (isAllowedProviderSafe(name)) { + if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) { + continue; + } + if (criteria != null && !LocationProvider.propertiesMeetCriteria( + name, provider.getProperties(), criteria)) { + continue; + } + out.add(name); } - out.add(name); } } + } finally { + Binder.restoreCallingIdentity(identity); } if (D) Log.d(TAG, "getProviders()=" + out); @@ -757,12 +792,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run LocationProviderInterface p = mProviders.get(i); boolean isEnabled = p.isEnabled(); String name = p.getName(); - boolean shouldBeEnabled = isAllowedBySettingsLocked(name); + boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId); if (isEnabled && !shouldBeEnabled) { - updateProviderListenersLocked(name, false); + updateProviderListenersLocked(name, false, mCurrentUserId); changesMade = true; } else if (!isEnabled && shouldBeEnabled) { - updateProviderListenersLocked(name, true); + updateProviderListenersLocked(name, true, mCurrentUserId); changesMade = true; } } @@ -772,7 +807,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private void updateProviderListenersLocked(String provider, boolean enabled) { + private void updateProviderListenersLocked(String provider, boolean enabled, int userId) { int listeners = 0; LocationProviderInterface p = mProvidersByName.get(provider); @@ -785,14 +820,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final int N = records.size(); for (int i = 0; i < N; i++) { UpdateRecord record = records.get(i); - // Sends a notification message to the receiver - if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) { - if (deadReceivers == null) { - deadReceivers = new ArrayList<Receiver>(); + if (UserHandle.getUserId(record.mReceiver.mUid) == userId) { + // Sends a notification message to the receiver + if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) { + if (deadReceivers == null) { + deadReceivers = new ArrayList<Receiver>(); + } + deadReceivers.add(record.mReceiver); } - deadReceivers.add(record.mReceiver); + listeners++; } - listeners++; } } @@ -822,12 +859,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (records != null) { for (UpdateRecord record : records) { - LocationRequest locationRequest = record.mRequest; - - providerRequest.locationRequests.add(locationRequest); - if (locationRequest.getInterval() < providerRequest.interval) { - providerRequest.reportLocation = true; - providerRequest.interval = locationRequest.getInterval(); + if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) { + LocationRequest locationRequest = record.mRequest; + providerRequest.locationRequests.add(locationRequest); + if (locationRequest.getInterval() < providerRequest.interval) { + providerRequest.reportLocation = true; + providerRequest.interval = locationRequest.getInterval(); + } } } @@ -839,9 +877,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // under that threshold. long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2; for (UpdateRecord record : records) { - LocationRequest locationRequest = record.mRequest; - if (locationRequest.getInterval() <= thresholdInterval) { - worksource.add(record.mReceiver.mUid); + if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) { + LocationRequest locationRequest = record.mRequest; + if (locationRequest.getInterval() <= thresholdInterval) { + worksource.add(record.mReceiver.mUid); + } } } } @@ -1063,7 +1103,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run oldRecord.disposeLocked(false); } - boolean isProviderEnabled = isAllowedBySettingsLocked(name); + boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid)); if (isProviderEnabled) { applyRequirementsLocked(name); } else { @@ -1120,7 +1160,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // update provider for (String provider : providers) { // If provider is already disabled, don't need to do anything - if (!isAllowedBySettingsLocked(provider)) { + if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) { continue; } @@ -1135,36 +1175,41 @@ public class LocationManagerService extends ILocationManager.Stub implements Run String perm = checkPermissionAndRequest(request); checkPackageName(packageName); - if (mBlacklist.isBlacklisted(packageName)) { - if (D) Log.d(TAG, "not returning last loc for blacklisted app: " + - packageName); - return null; - } - - synchronized (mLock) { - // Figure out the provider. Either its explicitly request (deprecated API's), - // or use the fused provider - String name = request.getProvider(); - if (name == null) name = LocationManager.FUSED_PROVIDER; - LocationProviderInterface provider = mProvidersByName.get(name); - if (provider == null) return null; - - if (!isAllowedBySettingsLocked(name)) return null; - - Location location = mLastLocation.get(name); - if (location == null) { + long identity = Binder.clearCallingIdentity(); + try { + if (mBlacklist.isBlacklisted(packageName)) { + if (D) Log.d(TAG, "not returning last loc for blacklisted app: " + + packageName); return null; } - if (ACCESS_FINE_LOCATION.equals(perm)) { - return location; - } else { - Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); - if (noGPSLocation != null) { - return mLocationFudger.getOrCreate(noGPSLocation); + + synchronized (mLock) { + // Figure out the provider. Either its explicitly request (deprecated API's), + // or use the fused provider + String name = request.getProvider(); + if (name == null) name = LocationManager.FUSED_PROVIDER; + LocationProviderInterface provider = mProvidersByName.get(name); + if (provider == null) return null; + + if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null; + + Location location = mLastLocation.get(name); + if (location == null) { + return null; + } + if (ACCESS_FINE_LOCATION.equals(perm)) { + return location; + } else { + Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); + if (noGPSLocation != null) { + return mLocationFudger.getOrCreate(noGPSLocation); + } } } + return null; + } finally { + Binder.restoreCallingIdentity(identity); } - return null; } @Override @@ -1292,14 +1337,24 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public boolean isProviderEnabled(String provider) { - checkPermission(); + String perms = checkPermission(); if (LocationManager.FUSED_PROVIDER.equals(provider)) return false; + if (ACCESS_COARSE_LOCATION.equals(perms) && + !isProviderAllowedByCoarsePermission(provider)) { + throw new SecurityException("The \"" + provider + + "\" provider requires ACCESS_FINE_LOCATION permission"); + } - synchronized (mLock) { - LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) return false; + long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + LocationProviderInterface p = mProvidersByName.get(provider); + if (p == null) return false; - return isAllowedBySettingsLocked(provider); + return isAllowedBySettingsLocked(provider, mCurrentUserId); + } + } finally { + Binder.restoreCallingIdentity(identity); } } @@ -1388,9 +1443,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run long now = SystemClock.elapsedRealtime(); String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider()); - ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); - if (records == null || records.size() == 0) return; + // Skip if the provider is unknown. LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) return; @@ -1411,6 +1465,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } lastLocation.set(location); + // Skip if there are no UpdateRecords for this provider. + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); + if (records == null || records.size() == 0) return; + // Fetch coarse location Location coarseLocation = null; if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) { @@ -1432,6 +1490,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run Receiver receiver = r.mReceiver; boolean receiverDead = false; + int receiverUserId = UserHandle.getUserId(receiver.mUid); + if (receiverUserId != mCurrentUserId) { + if (D) { + Log.d(TAG, "skipping loc update for background user " + receiverUserId + + " (current user: " + mCurrentUserId + ", app: " + + receiver.mPackageName + ")"); + } + continue; + } + if (mBlacklist.isBlacklisted(receiver.mPackageName)) { if (D) Log.d(TAG, "skipping loc update for blacklisted app: " + receiver.mPackageName); @@ -1522,7 +1590,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } synchronized (mLock) { - if (isAllowedBySettingsLocked(provider)) { + if (isAllowedBySettingsLocked(provider, mCurrentUserId)) { handleLocationChangedLocked(location, passive); } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 5d5f8d3..85b488c 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -1029,11 +1029,20 @@ public class NotificationManagerService extends INotificationManager.Stub } } + final int currentUser; + final long token = Binder.clearCallingIdentity(); + try { + currentUser = ActivityManager.getCurrentUser(); + } finally { + Binder.restoreCallingIdentity(token); + } + // If we're not supposed to beep, vibrate, etc. then don't. if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) && (!(old != null && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) - && (r.userId == UserHandle.USER_ALL || r.userId == userId) + && (r.userId == UserHandle.USER_ALL || + (r.userId == userId && r.userId == currentUser)) && mSystemReady) { final AudioManager audioManager = (AudioManager) mContext @@ -1240,7 +1249,7 @@ public class NotificationManagerService extends INotificationManager.Stub if ((r.notification.flags & mustNotHaveFlags) != 0) { continue; } - if (!r.pkg.equals(pkg)) { + if (pkg != null && !r.pkg.equals(pkg)) { continue; } canceledSomething = true; diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java index e99949b..5598b0a 100644 --- a/services/java/com/android/server/ServiceWatcher.java +++ b/services/java/com/android/server/ServiceWatcher.java @@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.os.Handler; import android.os.IBinder; +import android.os.UserHandle; import android.util.Log; import com.android.internal.content.PackageMonitor; @@ -58,15 +59,17 @@ public class ServiceWatcher implements ServiceConnection { private IBinder mBinder; // connected service private String mPackageName; // current best package private int mVersion; // current best version + private int mCurrentUserId; public ServiceWatcher(Context context, String logTag, String action, - List<String> initialPackageNames, Runnable newServiceWork, Handler handler) { + List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) { mContext = context; mTag = logTag; mAction = action; mPm = mContext.getPackageManager(); mNewServiceWork = newServiceWork; mHandler = handler; + mCurrentUserId = userId; mSignatureSets = new ArrayList<HashSet<Signature>>(); for (int i=0; i < initialPackageNames.size(); i++) { @@ -85,9 +88,11 @@ public class ServiceWatcher implements ServiceConnection { } public boolean start() { - if (!bindBestPackage(null)) return false; + synchronized (mLock) { + if (!bindBestPackageLocked(null)) return false; + } - mPackageMonitor.register(mContext, null, true); + mPackageMonitor.register(mContext, null, UserHandle.ALL, true); return true; } @@ -98,13 +103,13 @@ public class ServiceWatcher implements ServiceConnection { * is null. * Return true if a new package was found to bind to. */ - private boolean bindBestPackage(String justCheckThisPackage) { + private boolean bindBestPackageLocked(String justCheckThisPackage) { Intent intent = new Intent(mAction); if (justCheckThisPackage != null) { intent.setPackage(justCheckThisPackage); } - List<ResolveInfo> rInfos = mPm.queryIntentServices(new Intent(mAction), - PackageManager.GET_META_DATA); + List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction), + PackageManager.GET_META_DATA, mCurrentUserId); int bestVersion = Integer.MIN_VALUE; String bestPackage = null; for (ResolveInfo rInfo : rInfos) { @@ -141,36 +146,32 @@ public class ServiceWatcher implements ServiceConnection { (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage))); if (bestPackage != null) { - bindToPackage(bestPackage, bestVersion); + bindToPackageLocked(bestPackage, bestVersion); return true; } return false; } - private void unbind() { + private void unbindLocked() { String pkg; - synchronized (mLock) { - pkg = mPackageName; - mPackageName = null; - mVersion = Integer.MIN_VALUE; - } + pkg = mPackageName; + mPackageName = null; + mVersion = Integer.MIN_VALUE; if (pkg != null) { if (D) Log.d(mTag, "unbinding " + pkg); mContext.unbindService(this); } } - private void bindToPackage(String packageName, int version) { - unbind(); + private void bindToPackageLocked(String packageName, int version) { + unbindLocked(); Intent intent = new Intent(mAction); intent.setPackage(packageName); - synchronized (mLock) { - mPackageName = packageName; - mVersion = version; - } + mPackageName = packageName; + mVersion = version; if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")"); mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE); + | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId); } private boolean isSignatureMatch(Signature[] signatures) { @@ -197,31 +198,37 @@ public class ServiceWatcher implements ServiceConnection { */ @Override public void onPackageUpdateFinished(String packageName, int uid) { - if (packageName.equals(mPackageName)) { - // package updated, make sure to rebind - unbind(); + synchronized (mLock) { + if (packageName.equals(mPackageName)) { + // package updated, make sure to rebind + unbindLocked(); + } + // check the updated package in case it is better + bindBestPackageLocked(packageName); } - // check the updated package in case it is better - bindBestPackage(packageName); } @Override public void onPackageAdded(String packageName, int uid) { - if (packageName.equals(mPackageName)) { - // package updated, make sure to rebind - unbind(); + synchronized (mLock) { + if (packageName.equals(mPackageName)) { + // package updated, make sure to rebind + unbindLocked(); + } + // check the new package is case it is better + bindBestPackageLocked(packageName); } - // check the new package is case it is better - bindBestPackage(packageName); } @Override public void onPackageRemoved(String packageName, int uid) { - if (packageName.equals(mPackageName)) { - unbind(); - // the currently bound package was removed, - // need to search for a new package - bindBestPackage(null); + synchronized (mLock) { + if (packageName.equals(mPackageName)) { + unbindLocked(); + // the currently bound package was removed, + // need to search for a new package + bindBestPackageLocked(null); + } } } }; @@ -271,4 +278,12 @@ public class ServiceWatcher implements ServiceConnection { return mBinder; } } + + public void switchUser(int userId) { + synchronized (mLock) { + unbindLocked(); + mCurrentUserId = userId; + bindBestPackageLocked(null); + } + } } diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 5d9441b..439eebe 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -544,7 +544,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub int i; for (i=0; i<N; i++) { DisableRecord t = mDisableRecords.get(i); - if (t.token == token) { + if (t.token == token && t.userId == userId) { tok = t; break; } diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java index c74dd00..d0d8428 100644 --- a/services/java/com/android/server/TextServicesManagerService.java +++ b/services/java/com/android/server/TextServicesManagerService.java @@ -25,17 +25,25 @@ import com.android.internal.textservice.ITextServicesSessionListener; import org.xmlpull.v1.XmlPullParserException; +import android.app.ActivityManagerNative; +import android.app.AppGlobals; +import android.app.IUserSwitchObserver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.IRemoteCallback; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.Settings; import android.service.textservice.SpellCheckerService; import android.text.TextUtils; @@ -66,6 +74,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<SpellCheckerInfo>(); private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = new HashMap<String, SpellCheckerBindGroup>(); + private final TextServicesSettings mSettings; public void systemReady() { if (!mSystemReady) { @@ -76,11 +85,43 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public TextServicesManagerService(Context context) { mSystemReady = false; mContext = context; + int userId = UserHandle.USER_OWNER; + try { + ActivityManagerNative.getDefault().registerUserSwitchObserver( + new IUserSwitchObserver.Stub() { + @Override + public void onUserSwitching(int newUserId, IRemoteCallback reply) { + synchronized(mSpellCheckerMap) { + switchUserLocked(newUserId); + } + if (reply != null) { + try { + reply.sendResult(null); + } catch (RemoteException e) { + } + } + } + + @Override + public void onUserSwitchComplete(int newUserId) throws RemoteException { + } + }); + userId = ActivityManagerNative.getDefault().getCurrentUser().id; + } catch (RemoteException e) { + Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); + } mMonitor = new TextServicesMonitor(); mMonitor.register(context, null, true); - synchronized (mSpellCheckerMap) { - buildSpellCheckerMapLocked(context, mSpellCheckerList, mSpellCheckerMap); - } + mSettings = new TextServicesSettings(context.getContentResolver(), userId); + + // "switchUserLocked" initializes the states for the foreground user + switchUserLocked(userId); + } + + private void switchUserLocked(int userId) { + mSettings.setCurrentUserId(userId); + unbindServiceLocked(); + buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); SpellCheckerInfo sci = getCurrentSpellChecker(null); if (sci == null) { sci = findAvailSpellCheckerLocked(null, null); @@ -94,10 +135,23 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } private class TextServicesMonitor extends PackageMonitor { + private boolean isChangingPackagesOfCurrentUser() { + final int userId = getChangingUserId(); + final boolean retval = userId == mSettings.getCurrentUserId(); + if (DBG) { + Slog.d(TAG, "--- ignore this call back from a background user: " + userId); + } + return retval; + } + @Override public void onSomePackagesChanged() { + if (!isChangingPackagesOfCurrentUser()) { + return; + } synchronized (mSpellCheckerMap) { - buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap); + buildSpellCheckerMapLocked( + mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); // TODO: Update for each locale SpellCheckerInfo sci = getCurrentSpellChecker(null); if (sci == null) return; @@ -117,12 +171,14 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } private static void buildSpellCheckerMapLocked(Context context, - ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map) { + ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map, + TextServicesSettings settings) { list.clear(); map.clear(); final PackageManager pm = context.getPackageManager(); - List<ResolveInfo> services = pm.queryIntentServices( - new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA); + final List<ResolveInfo> services = pm.queryIntentServicesAsUser( + new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA, + settings.getCurrentUserId()); final int N = services.size(); for (int i = 0; i < N; ++i) { final ResolveInfo ri = services.get(i); @@ -155,6 +211,53 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } + // --------------------------------------------------------------------------------------- + // Check whether or not this is a valid IPC. Assumes an IPC is valid when either + // 1) it comes from the system process + // 2) the calling process' user id is identical to the current user id TSMS thinks. + private boolean calledFromValidUser() { + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + if (DBG) { + Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? " + + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID + + " calling userId = " + userId + ", foreground user id = " + + mSettings.getCurrentUserId()); + try { + final String[] packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); + for (int i = 0; i < packageNames.length; ++i) { + if (DBG) { + Slog.d(TAG, "--- process name for "+ uid + " = " + packageNames[i]); + } + } + } catch (RemoteException e) { + } + } + + if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) { + return true; + } else { + Slog.w(TAG, "--- IPC called from background users. Ignore. \n" + getStackTrace()); + return false; + } + } + + private boolean bindCurrentSpellCheckerService( + Intent service, ServiceConnection conn, int flags) { + if (service == null || conn == null) { + Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn); + return false; + } + return mContext.bindService(service, conn, flags, mSettings.getCurrentUserId()); + } + + private void unbindServiceLocked() { + for (SpellCheckerBindGroup scbg : mSpellCheckerBindGroups.values()) { + scbg.removeAll(); + } + mSpellCheckerBindGroups.clear(); + } + // TODO: find an appropriate spell checker for specified locale private SpellCheckerInfo findAvailSpellCheckerLocked(String locale, String prefPackage) { final int spellCheckersCount = mSpellCheckerList.size(); @@ -183,10 +286,12 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { // checker is saved. @Override public SpellCheckerInfo getCurrentSpellChecker(String locale) { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return null; + } synchronized (mSpellCheckerMap) { - final String curSpellCheckerId = - Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.SELECTED_SPELL_CHECKER); + final String curSpellCheckerId = mSettings.getSelectedSpellChecker(); if (DBG) { Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId); } @@ -202,10 +307,12 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public SpellCheckerSubtype getCurrentSpellCheckerSubtype( String locale, boolean allowImplicitlySelectedSubtype) { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return null; + } synchronized (mSpellCheckerMap) { - final String subtypeHashCodeStr = - Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE); + final String subtypeHashCodeStr = mSettings.getSelectedSpellCheckerSubtype(); if (DBG) { Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCodeStr); } @@ -280,6 +387,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public void getSpellCheckerService(String sciId, String locale, ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener, Bundle bundle) { + if (!calledFromValidUser()) { + return; + } if (!mSystemReady) { return; } @@ -346,6 +456,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public boolean isSpellCheckerEnabled() { + if (!calledFromValidUser()) { + return false; + } synchronized(mSpellCheckerMap) { return isSpellCheckerEnabledLocked(); } @@ -365,7 +478,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (DBG) { Slog.w(TAG, "bind service: " + info.getId()); } - if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { + if (!bindCurrentSpellCheckerService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { Slog.e(TAG, "Failed to get a spell checker service."); return; } @@ -376,6 +489,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public SpellCheckerInfo[] getEnabledSpellCheckers() { + // TODO: Make this work even for non-current users? + if (!calledFromValidUser()) { + return null; + } if (DBG) { Slog.d(TAG, "getEnabledSpellCheckers: " + mSpellCheckerList.size()); for (int i = 0; i < mSpellCheckerList.size(); ++i) { @@ -387,6 +504,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void finishSpellCheckerService(ISpellCheckerSessionListener listener) { + if (!calledFromValidUser()) { + return; + } if (DBG) { Slog.d(TAG, "FinishSpellCheckerService"); } @@ -407,6 +527,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void setCurrentSpellChecker(String locale, String sciId) { + if (!calledFromValidUser()) { + return; + } synchronized(mSpellCheckerMap) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) @@ -421,6 +544,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void setCurrentSpellCheckerSubtype(String locale, int hashCode) { + if (!calledFromValidUser()) { + return; + } synchronized(mSpellCheckerMap) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) @@ -435,6 +561,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void setSpellCheckerEnabled(boolean enabled) { + if (!calledFromValidUser()) { + return; + } synchronized(mSpellCheckerMap) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) @@ -459,8 +588,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } final long ident = Binder.clearCallingIdentity(); try { - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.SELECTED_SPELL_CHECKER, sciId); + mSettings.putSelectedSpellChecker(sciId); setCurrentSpellCheckerSubtypeLocked(0); } finally { Binder.restoreCallingIdentity(ident); @@ -481,8 +609,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } final long ident = Binder.clearCallingIdentity(); try { - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(tempHashCode)); + mSettings.putSelectedSpellCheckerSubtype(tempHashCode); } finally { Binder.restoreCallingIdentity(ident); } @@ -494,8 +621,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } final long ident = Binder.clearCallingIdentity(); try { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.SPELL_CHECKER_ENABLED, enabled ? 1 : 0); + mSettings.setSpellCheckerEnabled(enabled); } finally { Binder.restoreCallingIdentity(ident); } @@ -504,8 +630,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private boolean isSpellCheckerEnabledLocked() { final long ident = Binder.clearCallingIdentity(); try { - final boolean retval = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SPELL_CHECKER_ENABLED, 1) == 1; + final boolean retval = mSettings.isSpellCheckerEnabled(); if (DBG) { Slog.w(TAG, "getSpellCheckerEnabled: " + retval); } @@ -729,14 +854,19 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized(mSpellCheckerMap) { - if (DBG) { - Slog.w(TAG, "onServiceConnected: " + name); - } - ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service); - final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); - if (group != null && this == group.mInternalConnection) { - group.onServiceConnected(spellChecker); - } + onServiceConnectedInnerLocked(name, service); + } + } + + private void onServiceConnectedInnerLocked(ComponentName name, IBinder service) { + if (DBG) { + Slog.w(TAG, "onServiceConnected: " + name); + } + final ISpellCheckerService spellChecker = + ISpellCheckerService.Stub.asInterface(service); + final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); + if (group != null && this == group.mInternalConnection) { + group.onServiceConnected(spellChecker); } } @@ -778,4 +908,73 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { mGroup.removeListener(mScListener); } } + + private static class TextServicesSettings { + private final ContentResolver mResolver; + private int mCurrentUserId; + public TextServicesSettings(ContentResolver resolver, int userId) { + mResolver = resolver; + mCurrentUserId = userId; + } + + public void setCurrentUserId(int userId) { + if (DBG) { + Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " + + userId + ", new ime = " + getSelectedSpellChecker()); + } + // TSMS settings are kept per user, so keep track of current user + mCurrentUserId = userId; + } + + public int getCurrentUserId() { + return mCurrentUserId; + } + + public void putSelectedSpellChecker(String sciId) { + Settings.Secure.putStringForUser(mResolver, + Settings.Secure.SELECTED_SPELL_CHECKER, sciId, mCurrentUserId); + } + + public void putSelectedSpellCheckerSubtype(int hashCode) { + Settings.Secure.putStringForUser(mResolver, + Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode), + mCurrentUserId); + } + + public void setSpellCheckerEnabled(boolean enabled) { + Settings.Secure.putIntForUser(mResolver, + Settings.Secure.SPELL_CHECKER_ENABLED, enabled ? 1 : 0, mCurrentUserId); + } + + public String getSelectedSpellChecker() { + return Settings.Secure.getStringForUser(mResolver, + Settings.Secure.SELECTED_SPELL_CHECKER, mCurrentUserId); + } + + public String getSelectedSpellCheckerSubtype() { + return Settings.Secure.getStringForUser(mResolver, + Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, mCurrentUserId); + } + + public boolean isSpellCheckerEnabled() { + return Settings.Secure.getIntForUser(mResolver, + Settings.Secure.SPELL_CHECKER_ENABLED, 1, mCurrentUserId) == 1; + } + } + + // ---------------------------------------------------------------------- + // Utilities for debug + private static String getStackTrace() { + final StringBuilder sb = new StringBuilder(); + try { + throw new RuntimeException(); + } catch (RuntimeException e) { + final StackTraceElement[] frames = e.getStackTrace(); + // Start at 1 because the first frame is here and we don't care about it + for (int j = 1; j < frames.length; ++j) { + sb.append(frames[j].toString() + "\n"); + } + } + return sb.toString(); + } } diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index d1af2b0..e9e3163 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -37,6 +37,7 @@ import android.os.Handler; import android.os.PowerManager; 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; @@ -90,6 +91,8 @@ class UiModeManagerService extends IUiModeManager.Stub { private NotificationManager mNotificationManager; private StatusBarManager mStatusBarManager; + + private final PowerManager mPowerManager; private final PowerManager.WakeLock mWakeLock; static Intent buildHomeIntent(String category) { @@ -163,8 +166,8 @@ class UiModeManagerService extends IUiModeManager.Stub { mContext.registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); + mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); mConfiguration.setToDefaults(); @@ -502,7 +505,17 @@ class UiModeManagerService extends IUiModeManager.Stub { try { IDreamManager dreamManagerService = IDreamManager.Stub.asInterface( ServiceManager.getService(DreamService.DREAM_SERVICE)); - dreamManagerService.dream(); + if (dreamManagerService != null && !dreamManagerService.isDreaming()) { + // Wake up. + // The power manager will wake up the system when it starts receiving power + // but there is a race between that happening and the UI mode manager + // starting a dream. We want the system to already be awake + // by the time this happens. Otherwise the dream may not start. + mPowerManager.wakeUp(SystemClock.uptimeMillis()); + + // Dream. + dreamManagerService.dream(); + } } catch (RemoteException ex) { Slog.e(TAG, "Could not start dream when docked.", ex); } diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 4225913..e0f3814 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -38,6 +38,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; @@ -53,6 +54,7 @@ import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.IWallpaperService; @@ -97,6 +99,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub { static final String WALLPAPER_INFO = "wallpaper_info.xml"; /** + * Name of the component used to display bitmap wallpapers from either the gallery or + * built-in wallpapers. + */ + static final ComponentName IMAGE_WALLPAPER = new ComponentName("com.android.systemui", + "com.android.systemui.ImageWallpaper"); + + /** * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks * that the wallpaper has changed. The CREATE is triggered when there is no * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered @@ -136,7 +145,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (event == CLOSE_WRITE) { mWallpaper.imageWallpaperPending = false; } - bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true, + bindWallpaperComponentLocked(IMAGE_WALLPAPER, true, false, mWallpaper, null); saveSettingsLocked(mWallpaper); } @@ -181,13 +190,6 @@ class WallpaperManagerService extends IWallpaperManager.Stub { */ ComponentName nextWallpaperComponent; - /** - * Name of the component used to display bitmap wallpapers from either the gallery or - * built-in wallpapers. - */ - ComponentName imageWallpaperComponent = new ComponentName("com.android.systemui", - "com.android.systemui.ImageWallpaper"); - WallpaperConnection connection; long lastDiedTime; boolean wallpaperUpdating; @@ -511,6 +513,9 @@ class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper = new WallpaperData(userId); mWallpaperMap.put(userId, wallpaper); loadSettingsLocked(userId); + } + // Not started watching yet, in case wallpaper data was loaded for other reasons. + if (wallpaper.wallpaperObserver == null) { wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); wallpaper.wallpaperObserver.startWatching(); } @@ -554,7 +559,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.imageWallpaperPending = false; if (userId != mCurrentUserId) return; if (bindWallpaperComponentLocked(defaultFailed - ? wallpaper.imageWallpaperComponent + ? IMAGE_WALLPAPER : null, true, false, wallpaper, reply)) { return; } @@ -580,9 +585,21 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public boolean hasNamedWallpaper(String name) { synchronized (mLock) { - for (int i=0; i<mWallpaperMap.size(); i++) { - WallpaperData wd = mWallpaperMap.valueAt(i); - if (name.equals(wd.name)) { + List<UserInfo> users; + long ident = Binder.clearCallingIdentity(); + try { + users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers(); + } finally { + Binder.restoreCallingIdentity(ident); + } + for (UserInfo user: users) { + WallpaperData wd = mWallpaperMap.get(user.id); + if (wd == null) { + // User hasn't started yet, so load her settings to peek at the wallpaper + loadSettingsLocked(user.id); + wd = mWallpaperMap.get(user.id); + } + if (wd != null && name.equals(wd.name)) { return true; } } @@ -775,7 +792,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } if (componentName == null) { // Fall back to static image wallpaper - componentName = wallpaper.imageWallpaperComponent; + componentName = IMAGE_WALLPAPER; //clearWallpaperComponentLocked(); //return; if (DEBUG) Slog.v(TAG, "Using image wallpaper"); @@ -798,7 +815,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { WallpaperInfo wi = null; Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); - if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) { + if (componentName != null && !componentName.equals(IMAGE_WALLPAPER)) { // Make sure the selected service is actually a wallpaper service. List<ResolveInfo> ris = mIPackageManager.queryIntentServices(intent, @@ -973,7 +990,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { out.attribute(null, "height", Integer.toString(wallpaper.height)); out.attribute(null, "name", wallpaper.name); if (wallpaper.wallpaperComponent != null - && !wallpaper.wallpaperComponent.equals(wallpaper.imageWallpaperComponent)) { + && !wallpaper.wallpaperComponent.equals(IMAGE_WALLPAPER)) { out.attribute(null, "component", wallpaper.wallpaperComponent.flattenToShortString()); } @@ -1045,7 +1062,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.nextWallpaperComponent == null || "android".equals(wallpaper.nextWallpaperComponent .getPackageName())) { - wallpaper.nextWallpaperComponent = wallpaper.imageWallpaperComponent; + wallpaper.nextWallpaperComponent = IMAGE_WALLPAPER; } if (DEBUG) { @@ -1107,7 +1124,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { loadSettingsLocked(0); wallpaper = mWallpaperMap.get(0); if (wallpaper.nextWallpaperComponent != null - && !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) { + && !wallpaper.nextWallpaperComponent.equals(IMAGE_WALLPAPER)) { if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false, wallpaper, null)) { // No such live wallpaper or other failure; fall back to the default diff --git a/services/java/com/android/server/WiredAccessoryManager.java b/services/java/com/android/server/WiredAccessoryManager.java index 63e8895..d5c9c8f 100644 --- a/services/java/com/android/server/WiredAccessoryManager.java +++ b/services/java/com/android/server/WiredAccessoryManager.java @@ -152,7 +152,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { break; } - updateLocked(NAME_H2W, headset); + updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)) | headset); } } diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 5e9e223..3d77b3a 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -643,6 +643,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return mSecurityPolicy.mActiveWindowId; } + void onTouchInteractionEnd() { + mSecurityPolicy.onTouchInteractionEnd(); + } + private void switchUser(int userId) { synchronized (mLock) { // The user switched so we do not need to restore the current user @@ -1119,7 +1123,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { R.string.enable_explore_by_touch_warning_message, label)) .create(); mEnableTouchExplorationDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); + WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags + |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true); mEnableTouchExplorationDialog.show(); } @@ -2178,16 +2184,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mActiveWindowId = windowId; } } break; - case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: - case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { + case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: { mActiveWindowId = windowId; } break; - case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END: { - mActiveWindowId = getFocusedWindowId(); - } break; } } + public void onTouchInteractionEnd() { + // We want to set the active window to be current immediately + // after the user has stopped touching the screen since if the + // user types with the IME he should get a feedback for the + // letter typed in the text view which is in the input focused + // window. Note that we always deliver hover accessibility events + // (they are a result of user touching the screen) so change of + // the active window before all hover accessibility events from + // the touched window are delivered is fine. + mActiveWindowId = getFocusedWindowId(); + } + public int getRetrievalAllowingWindowLocked() { return mActiveWindowId; } diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java index 14762a1..51ccd47 100644 --- a/services/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java @@ -846,7 +846,6 @@ public final class ScreenMagnifier implements EventStreamTransformation { private static final class DisplayContentObserver { private static final int MESSAGE_SHOW_VIEWPORT_FRAME = 1; - private static final int MESSAGE_RECOMPUTE_VIEWPORT_BOUNDS = 2; private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 3; private static final int MESSAGE_ON_WINDOW_TRANSITION = 4; private static final int MESSAGE_ON_ROTATION_CHANGED = 5; @@ -892,7 +891,9 @@ public final class ScreenMagnifier implements EventStreamTransformation { || info.type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG && (transition == WindowManagerPolicy.TRANSIT_EXIT || transition == WindowManagerPolicy.TRANSIT_HIDE)) { - mHandler.sendMessageDelayed(message, mLongAnimationDuration); + final long delay = (long) (2 * mLongAnimationDuration + * mWindowAnimationScale); + mHandler.sendMessageDelayed(message, delay); } else { message.sendToTarget(); } @@ -1009,7 +1010,8 @@ public final class ScreenMagnifier implements EventStreamTransformation { case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { Rect magnifiedRegionBounds = mMagnificationController .getMagnifiedRegionBounds(); Rect touchableRegion = info.touchableRegion; @@ -1169,10 +1171,6 @@ public final class ScreenMagnifier implements EventStreamTransformation { case MESSAGE_SHOW_VIEWPORT_FRAME: { mViewport.setFrameShown(true, true); } break; - case MESSAGE_RECOMPUTE_VIEWPORT_BOUNDS: { - final boolean animate = message.arg1 == 1; - mViewport.recomputeBounds(animate); - } break; case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: { SomeArgs args = (SomeArgs) message.obj; try { @@ -1525,8 +1523,10 @@ public final class ScreenMagnifier implements EventStreamTransformation { Rect magnifiedFrame = mTempRect1; magnifiedFrame.set(0, 0, 0, 0); - Rect notMagnifiedFrame = mTempRect2; - notMagnifiedFrame.set(0, 0, 0, 0); + DisplayInfo displayInfo = mDisplayProvider.getDisplayInfo(); + + Rect availableFrame = mTempRect2; + availableFrame.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); ArrayList<WindowInfo> infos = mTempWindowInfoList; infos.clear(); @@ -1541,18 +1541,16 @@ public final class ScreenMagnifier implements EventStreamTransformation { if (info.type == WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY) { continue; } + Rect windowFrame = mTempRect3; + windowFrame.set(info.touchableRegion); if (isWindowMagnified(info.type)) { - Rect clippedFrame = mTempRect3; - clippedFrame.set(info.touchableRegion); - subtract(clippedFrame, notMagnifiedFrame); - magnifiedFrame.union(clippedFrame); + magnifiedFrame.union(windowFrame); + magnifiedFrame.intersect(availableFrame); } else { - Rect clippedFrame = mTempRect3; - clippedFrame.set(info.touchableRegion); - subtract(clippedFrame, magnifiedFrame); - notMagnifiedFrame.union(clippedFrame); + subtract(windowFrame, magnifiedFrame); + subtract(availableFrame, windowFrame); } - if (magnifiedFrame.bottom >= notMagnifiedFrame.top) { + if (availableFrame.equals(magnifiedFrame)) { break; } } diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java index 2d81b6c..2688776 100644 --- a/services/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/java/com/android/server/accessibility/TouchExplorer.java @@ -102,10 +102,6 @@ class TouchExplorer implements EventStreamTransformation { // The timeout after which we are no longer trying to detect a gesture. private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000; - // The timeout to send interaction end events in case we did not - // receive the expected hover exit event due to a misbehaving app. - private static final int SEND_INTERACTION_END_EVENTS_TIMEOUT = 200; - // Temporary array for storing pointer IDs. private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT]; @@ -139,8 +135,11 @@ class TouchExplorer implements EventStreamTransformation { // Command for delayed sending of a hover exit event. private final SendHoverDelayed mSendHoverExitDelayed; - // Command for delayed sending of interaction ending events. - private final SendInteractionEndEventsDelayed mSendInteractionEndEventsDelayed; + // Command for delayed sending of touch exploration end events. + private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed; + + // Command for delayed sending of touch interaction end events. + private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed; // Command for delayed sending of a long press. private final PerformLongPressDelayed mPerformLongPressDelayed; @@ -209,11 +208,8 @@ class TouchExplorer implements EventStreamTransformation { // The id of the last touch explored window. private int mLastTouchedWindowId; - // Whether touch exploration gesture has ended. - private boolean mTouchExplorationGestureEnded; - - // Whether touch interaction has ended. - private boolean mTouchInteractionEnded; + // Whether touch exploration is in progress. + private boolean mTouchExplorationInProgress; /** * Creates a new instance. @@ -240,7 +236,12 @@ class TouchExplorer implements EventStreamTransformation { mGestureLibrary.load(); mSendHoverEnterDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_ENTER, true); mSendHoverExitDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_EXIT, false); - mSendInteractionEndEventsDelayed = new SendInteractionEndEventsDelayed(); + mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed( + AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END, + mDetermineUserIntentTimeout); + mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed( + AccessibilityEvent.TYPE_TOUCH_INTERACTION_END, + mDetermineUserIntentTimeout); mDoubleTapDetector = new DoubleTapDetector(); final float density = context.getResources().getDisplayMetrics().density; mScaledMinPointerDistanceToUseMiddleLocation = @@ -265,7 +266,7 @@ class TouchExplorer implements EventStreamTransformation { switch (mCurrentState) { case STATE_TOUCH_EXPLORING: { // If a touch exploration gesture is in progress send events for its end. - sendExitEventsIfNeeded(policyFlags); + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } break; case STATE_DRAGGING: { mDraggingPointerId = INVALID_POINTER_ID; @@ -286,7 +287,8 @@ class TouchExplorer implements EventStreamTransformation { mSendHoverExitDelayed.remove(); mPerformLongPressDelayed.remove(); mExitGestureDetectionModeDelayed.remove(); - mSendInteractionEndEventsDelayed.remove(); + mSendTouchExplorationEndDelayed.remove(); + mSendTouchInteractionEndDelayed.remove(); // Reset the pointer trackers. mReceivedPointerTracker.clear(); mInjectedPointerTracker.clear(); @@ -301,6 +303,8 @@ class TouchExplorer implements EventStreamTransformation { if (mNext != null) { mNext.clear(); } + mTouchExplorationInProgress = false; + mAms.onTouchInteractionEnd(); } @Override @@ -341,19 +345,17 @@ class TouchExplorer implements EventStreamTransformation { // The event for gesture end should be strictly after the // last hover exit event. - if (mTouchExplorationGestureEnded + if (mSendTouchExplorationEndDelayed.isPending() && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { - mSendInteractionEndEventsDelayed.remove(); - mTouchExplorationGestureEnded = false; + mSendTouchExplorationEndDelayed.remove(); sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); } // The event for touch interaction end should be strictly after the // last hover exit and the touch exploration gesture end events. - if (mTouchInteractionEnded + if (mSendTouchInteractionEndDelayed.isPending() && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { - mSendInteractionEndEventsDelayed.remove(); - mTouchInteractionEnded = false; + mSendTouchInteractionEndDelayed.remove(); sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); } @@ -396,15 +398,6 @@ class TouchExplorer implements EventStreamTransformation { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - // The delayed enter not delivered implies that we have delivered - // TYPE_TOUCH_INTERACTION_START and not TYPE_TOUCH_INTERACTION_END, - // therefore we need to deliver the interaction end event here. - if (mSendHoverEnterDelayed.isPending()) { - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); - } - // Announce the start of a new touch interaction. - sendAccessibilityEvent( - AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); // Pre-feed the motion events to the gesture detector since we // have a distance slop before getting into gesture detection // mode and not using the points within this slop significantly @@ -426,8 +419,20 @@ class TouchExplorer implements EventStreamTransformation { mSendHoverExitDelayed.remove(); } - if (mSendInteractionEndEventsDelayed.isPending()) { - mSendInteractionEndEventsDelayed.forceSendAndRemove(); + if (mSendTouchExplorationEndDelayed.isPending()) { + mSendTouchExplorationEndDelayed.forceSendAndRemove(); + } + + if (mSendTouchInteractionEndDelayed.isPending()) { + mSendTouchInteractionEndDelayed.forceSendAndRemove(); + } + + // Every pointer that goes down is active until it moves or + // another one goes down. Hence, having more than one pointer + // down we have already send the interaction start event. + if (event.getPointerCount() == 1) { + sendAccessibilityEvent( + AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); } mPerformLongPressDelayed.remove(); @@ -443,11 +448,13 @@ class TouchExplorer implements EventStreamTransformation { mPerformLongPressDelayed.post(event, policyFlags); break; } - // Deliver hover enter with a delay to have a chance - // to detect what the user is trying to do. - final int pointerId = receivedTracker.getPrimaryActivePointerId(); - final int pointerIdBits = (1 << pointerId); - mSendHoverEnterDelayed.post(event, true, pointerIdBits, policyFlags); + if (!mTouchExplorationInProgress) { + // Deliver hover enter with a delay to have a chance + // to detect what the user is trying to do. + final int pointerId = receivedTracker.getPrimaryActivePointerId(); + final int pointerIdBits = (1 << pointerId); + mSendHoverEnterDelayed.post(event, true, pointerIdBits, policyFlags); + } } break; default: { /* do nothing - let the code for ACTION_MOVE decide what to do */ @@ -512,12 +519,27 @@ class TouchExplorer implements EventStreamTransformation { break; } } else { + // Cancel the long press if pending and the user + // moved more than the slop. + if (mPerformLongPressDelayed.isPending()) { + final float deltaX = + receivedTracker.getReceivedPointerDownX(pointerId) + - rawEvent.getX(pointerIndex); + final float deltaY = + receivedTracker.getReceivedPointerDownY(pointerId) + - rawEvent.getY(pointerIndex); + final double moveDelta = Math.hypot(deltaX, deltaY); + // The user has moved enough for us to decide. + if (moveDelta > mTouchSlop) { + mPerformLongPressDelayed.remove(); + } + } // The user is wither double tapping or performing long // press so do not send move events yet. if (mDoubleTapDetector.firstTapDetected()) { break; } - sendEnterEventsIfNeeded(policyFlags); + sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); } @@ -548,7 +570,7 @@ class TouchExplorer implements EventStreamTransformation { } // We are sending events so send exit and gesture // end since we transition to another state. - sendExitEventsIfNeeded(policyFlags); + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } // We know that a new state transition is to happen and the @@ -583,7 +605,7 @@ class TouchExplorer implements EventStreamTransformation { mPerformLongPressDelayed.remove(); // We are sending events so send exit and gesture // end since we transition to another state. - sendExitEventsIfNeeded(policyFlags); + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } // More than two pointers are delegated to the view hierarchy. @@ -594,6 +616,7 @@ class TouchExplorer implements EventStreamTransformation { } } break; case MotionEvent.ACTION_UP: + mAms.onTouchInteractionEnd(); // We know that we do not need the pre-fed gesture points are not // needed anymore since the last pointer just went up. mStrokeBuffer.clear(); @@ -612,11 +635,14 @@ class TouchExplorer implements EventStreamTransformation { // If we have not delivered the enter schedule exit. if (mSendHoverEnterDelayed.isPending()) { - mSendHoverEnterDelayed.mTouchExplorationInProgress = false; mSendHoverExitDelayed.post(event, false, pointerIdBits, policyFlags); } else { // The user is touch exploring so we send events for end. - sendExitEventsIfNeeded(policyFlags); + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); + } + + if (!mSendTouchInteractionEndDelayed.isPending()) { + mSendTouchInteractionEndDelayed.post(); } } break; } @@ -713,6 +739,7 @@ class TouchExplorer implements EventStreamTransformation { } } break; case MotionEvent.ACTION_UP: { + mAms.onTouchInteractionEnd(); // Announce the end of a new touch interaction. sendAccessibilityEvent( AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); @@ -758,6 +785,7 @@ class TouchExplorer implements EventStreamTransformation { AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); //$FALL-THROUGH$ case MotionEvent.ACTION_POINTER_UP: { + mAms.onTouchInteractionEnd(); mLongPressingPointerId = -1; mLongPressingPointerDeltaX = 0; mLongPressingPointerDeltaY = 0; @@ -795,6 +823,7 @@ class TouchExplorer implements EventStreamTransformation { } } break; case MotionEvent.ACTION_UP: { + mAms.onTouchInteractionEnd(); // Announce the end of gesture recognition. sendAccessibilityEvent( AccessibilityEvent.TYPE_GESTURE_DETECTION_END); @@ -846,6 +875,14 @@ class TouchExplorer implements EventStreamTransformation { if (accessibilityManager.isEnabled()) { AccessibilityEvent event = AccessibilityEvent.obtain(type); accessibilityManager.sendAccessibilityEvent(event); + switch (type) { + case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: { + mTouchExplorationInProgress = true; + } break; + case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: { + mTouchExplorationInProgress = false; + } break; + } } } @@ -893,14 +930,12 @@ class TouchExplorer implements EventStreamTransformation { * * @param policyFlags The policy flags associated with the event. */ - private void sendExitEventsIfNeeded(int policyFlags) { + private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) { MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { final int pointerIdBits = event.getPointerIdBits(); - mTouchExplorationGestureEnded = true; - mTouchInteractionEnded = true; - if (!mSendInteractionEndEventsDelayed.isPending()) { - mSendInteractionEndEventsDelayed.post(); + if (!mSendTouchExplorationEndDelayed.isPending()) { + mSendTouchExplorationEndDelayed.post(); } sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); } @@ -912,10 +947,11 @@ class TouchExplorer implements EventStreamTransformation { * * @param policyFlags The policy flags associated with the event. */ - private void sendEnterEventsIfNeeded(int policyFlags) { + private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { final int pointerIdBits = event.getPointerIdBits(); + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); } } @@ -1181,8 +1217,12 @@ class TouchExplorer implements EventStreamTransformation { mSendHoverExitDelayed.remove(); mPerformLongPressDelayed.remove(); - // The touch interaction has ended since we will send a click. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + if (mSendTouchExplorationEndDelayed.isPending()) { + mSendTouchExplorationEndDelayed.forceSendAndRemove(); + } + if (mSendTouchInteractionEndDelayed.isPending()) { + mSendTouchInteractionEndDelayed.forceSendAndRemove(); + } int clickLocationX; int clickLocationY; @@ -1416,7 +1456,7 @@ class TouchExplorer implements EventStreamTransformation { mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocationX; mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocationY; - sendExitEventsIfNeeded(mPolicyFlags); + sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); mCurrentState = STATE_DELEGATING; sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags); @@ -1445,7 +1485,6 @@ class TouchExplorer implements EventStreamTransformation { private MotionEvent mPrototype; private int mPointerIdBits; private int mPolicyFlags; - private boolean mTouchExplorationInProgress; public SendHoverDelayed(int hoverAction, boolean gestureStarted) { mHoverAction = hoverAction; @@ -1456,7 +1495,6 @@ class TouchExplorer implements EventStreamTransformation { int pointerIdBits, int policyFlags) { remove(); mPrototype = MotionEvent.obtain(prototype); - mTouchExplorationInProgress = touchExplorationInProgress; mPointerIdBits = pointerIdBits; mPolicyFlags = policyFlags; mHandler.postDelayed(this, mDetermineUserIntentTimeout); @@ -1493,7 +1531,6 @@ class TouchExplorer implements EventStreamTransformation { mPrototype = null; mPointerIdBits = -1; mPolicyFlags = 0; - mTouchExplorationInProgress = false; } public void forceSendAndRemove() { @@ -1510,22 +1547,15 @@ class TouchExplorer implements EventStreamTransformation { Slog.d(LOG_TAG_SEND_HOVER_DELAYED, mGestureStarted ? "touchExplorationGestureStarted" : "touchExplorationGestureEnded"); } - if (mTouchExplorationInProgress) { - if (mGestureStarted) { - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); - } else { - mTouchExplorationGestureEnded = true; - mTouchInteractionEnded = true; - if (!mSendInteractionEndEventsDelayed.isPending()) { - mSendInteractionEndEventsDelayed.post(); - } - } + if (mGestureStarted) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); } else { - if (!mGestureStarted) { - mTouchInteractionEnded = true; - if (!mSendInteractionEndEventsDelayed.isPending()) { - mSendInteractionEndEventsDelayed.post(); - } + if (!mSendTouchExplorationEndDelayed.isPending()) { + mSendTouchExplorationEndDelayed.post(); + } + if (mSendTouchInteractionEndDelayed.isPending()) { + mSendTouchInteractionEndDelayed.remove(); + mSendTouchInteractionEndDelayed.post(); } } sendMotionEvent(mPrototype, mHoverAction, mPointerIdBits, mPolicyFlags); @@ -1533,14 +1563,21 @@ class TouchExplorer implements EventStreamTransformation { } } - private class SendInteractionEndEventsDelayed implements Runnable { + private class SendAccessibilityEventDelayed implements Runnable { + private final int mEventType; + private final int mDelay; + + public SendAccessibilityEventDelayed(int eventType, int delay) { + mEventType = eventType; + mDelay = delay; + } public void remove() { mHandler.removeCallbacks(this); } public void post() { - mHandler.postDelayed(this, SEND_INTERACTION_END_EVENTS_TIMEOUT); + mHandler.postDelayed(this, mDelay); } public boolean isPending() { @@ -1556,14 +1593,7 @@ class TouchExplorer implements EventStreamTransformation { @Override public void run() { - if (mTouchExplorationGestureEnded) { - mTouchExplorationGestureEnded = false; - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); - } - if (mTouchInteractionEnded) { - mTouchInteractionEnded = false; - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); - } + sendAccessibilityEvent(mEventType); } } diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index 1269433..35999ea 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -248,8 +248,9 @@ public class ActiveServices { synchronized (r.stats.getBatteryStats()) { r.stats.startRunningLocked(); } - if (!bringUpServiceLocked(r, service.getFlags(), false)) { - return new ComponentName("!", "Service process is bad"); + String error = bringUpServiceLocked(r, service.getFlags(), false); + if (error != null) { + return new ComponentName("!!", error); } return r.name; } @@ -518,7 +519,7 @@ public class ActiveServices { if ((flags&Context.BIND_AUTO_CREATE) != 0) { s.lastActivity = SystemClock.uptimeMillis(); - if (!bringUpServiceLocked(s, service.getFlags(), false)) { + if (bringUpServiceLocked(s, service.getFlags(), false) != null) { return 0; } } @@ -964,19 +965,19 @@ public class ActiveServices { return true; } - private final boolean bringUpServiceLocked(ServiceRecord r, + private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean whileRestarting) { //Slog.i(TAG, "Bring up service:"); //r.dump(" "); if (r.app != null && r.app.thread != null) { sendServiceArgsLocked(r, false); - return true; + return null; } if (!whileRestarting && r.restartDelay > 0) { // If waiting for a restart, then do nothing. - return true; + return null; } if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent); @@ -988,12 +989,13 @@ public class ActiveServices { // Make sure that the user who owns this service is started. If not, // we don't want to allow it to run. if (mAm.mStartedUsers.get(r.userId) == null) { - Slog.w(TAG, "Unable to launch app " + String msg = "Unable to launch app " + r.appInfo.packageName + "/" + r.appInfo.uid + " for service " - + r.intent.getIntent() + ": user " + r.userId + " is stopped"); + + r.intent.getIntent() + ": user " + r.userId + " is stopped"; + Slog.w(TAG, msg); bringDownServiceLocked(r, true); - return false; + return msg; } // Service is now being launched, its package can't be stopped. @@ -1018,7 +1020,7 @@ public class ActiveServices { try { app.addPackage(r.appInfo.packageName); realStartServiceLocked(r, app); - return true; + return null; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting service " + r.shortName, e); } @@ -1041,12 +1043,13 @@ public class ActiveServices { if (app == null) { if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, "service", r.name, false, isolated)) == null) { - Slog.w(TAG, "Unable to launch app " + String msg = "Unable to launch app " + r.appInfo.packageName + "/" + r.appInfo.uid + " for service " - + r.intent.getIntent() + ": process is bad"); + + r.intent.getIntent() + ": process is bad"; + Slog.w(TAG, msg); bringDownServiceLocked(r, true); - return false; + return msg; } if (isolated) { r.isolatedProc = app; @@ -1057,7 +1060,7 @@ public class ActiveServices { mPendingServices.add(r); } - return true; + return null; } private final void requestServiceBindingsLocked(ServiceRecord r) { @@ -1850,7 +1853,7 @@ public class ActiveServices { } if (anrMessage != null) { - mAm.appNotResponding(proc, null, null, anrMessage); + mAm.appNotResponding(proc, null, null, false, anrMessage); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index e90eef9..daed0a2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import com.android.internal.R; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessStats; +import com.android.internal.widget.LockPatternUtils; import com.android.server.AttributeCache; import com.android.server.IntentResolver; import com.android.server.ProcessMap; @@ -970,7 +971,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (mShowDialogs) { Dialog d = new AppNotRespondingDialog(ActivityManagerService.this, - mContext, proc, (ActivityRecord)data.get("activity")); + mContext, proc, (ActivityRecord)data.get("activity"), + msg.arg1 != 0); d.show(); proc.anrDialog = d; } else { @@ -3247,7 +3249,7 @@ public final class ActivityManagerService extends ActivityManagerNative } final void appNotResponding(ProcessRecord app, ActivityRecord activity, - ActivityRecord parent, final String annotation) { + ActivityRecord parent, boolean aboveSystem, final String annotation) { ArrayList<Integer> firstPids = new ArrayList<Integer>(5); SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); @@ -3388,6 +3390,7 @@ public final class ActivityManagerService extends ActivityManagerNative HashMap map = new HashMap(); msg.what = SHOW_NOT_RESPONDING_MSG; msg.obj = map; + msg.arg1 = aboveSystem ? 1 : 0; map.put("app", app); if (activity != null) { map.put("activity", activity); @@ -3582,7 +3585,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Failed trying to unstop package " + packageName + ": " + e); } - if (isUserRunningLocked(user)) { + if (isUserRunningLocked(user, false)) { forceStopPackageLocked(packageName, pkgUid); } } @@ -7340,6 +7343,51 @@ public final class ActivityManagerService extends ActivityManagerNative SystemProperties.set("ctl.start", "bugreport"); } + public long inputDispatchingTimedOut(int pid, boolean aboveSystem) { + if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.FILTER_EVENTS); + } + + ProcessRecord proc; + + // TODO: Unify this code with ActivityRecord.keyDispatchingTimedOut(). + synchronized (this) { + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(pid); + } + if (proc != null) { + if (proc.debugging) { + return -1; + } + + if (mDidDexOpt) { + // Give more time since we were dexopting. + mDidDexOpt = false; + return -1; + } + + if (proc.instrumentationClass != null) { + Bundle info = new Bundle(); + info.putString("shortMsg", "keyDispatchingTimedOut"); + info.putString("longMsg", "Timed out while dispatching key event"); + finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info); + proc = null; + } + } + } + + if (proc != null) { + appNotResponding(proc, null, null, aboveSystem, "keyDispatchingTimedOut"); + if (proc.instrumentationClass != null || proc.usingWrapper) { + return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT; + } + } + + return KEY_DISPATCHING_TIMEOUT; + } + public void registerProcessObserver(IProcessObserver observer) { enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, "registerProcessObserver()"); @@ -7406,6 +7454,7 @@ public final class ActivityManagerService extends ActivityManagerNative lp.format = v.getBackground().getOpacity(); lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; ((WindowManager)mContext.getSystemService( Context.WINDOW_SERVICE)).addView(v, lp); } @@ -7856,6 +7905,19 @@ public final class ActivityManagerService extends ActivityManagerNative broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId); + intent = new Intent(Intent.ACTION_USER_STARTING); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId); + broadcastIntentLocked(null, null, intent, + null, new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky, int sendingUser) + throws RemoteException { + } + }, 0, null, null, + android.Manifest.permission.INTERACT_ACROSS_USERS, + false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } finally { Binder.restoreCallingIdentity(ident); } @@ -8831,7 +8893,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" [-a] [-c] [-h] [cmd] ..."); pw.println(" cmd may be one of:"); pw.println(" a[ctivities]: activity stack state"); - pw.println(" b[roadcasts] [PACKAGE_NAME]: broadcast state"); + pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state"); pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state"); pw.println(" p[rocesses] [PACKAGE_NAME]: process state"); pw.println(" o[om]: out of memory management"); @@ -9290,6 +9352,12 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" User #"); pw.print(uss.mHandle.getIdentifier()); pw.print(": "); uss.dump("", pw); } + pw.print(" mStartedUserArray: ["); + for (int i=0; i<mStartedUserArray.length; i++) { + if (i > 0) pw.print(", "); + pw.print(mStartedUserArray[i]); + } + pw.println("]"); pw.print(" mUserLru: ["); for (int i=0; i<mUserLru.size(); i++) { if (i > 0) pw.print(", "); @@ -9659,6 +9727,9 @@ public final class ActivityManagerService extends ActivityManagerNative boolean onlyHistory = false; if ("history".equals(dumpPackage)) { + if (opti < args.length && "-s".equals(args[opti])) { + dumpAll = false; + } onlyHistory = true; dumpPackage = null; } @@ -14055,15 +14126,17 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } - mWindowManager.lockNow(); mWindowManager.startFreezingScreen(R.anim.screen_user_exit, R.anim.screen_user_enter); + boolean needStart = false; + // If the user we are switching to is not currently started, then // we need to start it now. if (mStartedUsers.get(userId) == null) { mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false)); updateStartedUserArrayLocked(); + needStart = true; } mCurrentUserId = userId; @@ -14074,25 +14147,48 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager.setCurrentUser(userId); + // Once the internal notion of the active user has switched, we lock the device + // with the option to show the user switcher on the keyguard. + mWindowManager.lockNow(LockPatternUtils.USER_SWITCH_LOCK_OPTIONS); + final UserStartedState uss = mStartedUsers.get(userId); + // Make sure user is in the started state. If it is currently + // stopping, we need to knock that off. + if (uss.mState == UserStartedState.STATE_STOPPING) { + // If we are stopping, we haven't sent ACTION_SHUTDOWN, + // so we can just fairly silently bring the user back from + // the almost-dead. + uss.mState = UserStartedState.STATE_RUNNING; + updateStartedUserArrayLocked(); + needStart = true; + } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) { + // This means ACTION_SHUTDOWN has been sent, so we will + // need to treat this as a new boot of the user. + uss.mState = UserStartedState.STATE_BOOTING; + updateStartedUserArrayLocked(); + needStart = true; + } + mHandler.removeMessages(REPORT_USER_SWITCH_MSG); mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG, oldUserId, userId, uss)); mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG, oldUserId, userId, uss), USER_SWITCH_TIMEOUT); - Intent intent = new Intent(Intent.ACTION_USER_STARTED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - broadcastIntentLocked(null, null, intent, - null, null, 0, null, null, null, - false, false, MY_PID, Process.SYSTEM_UID, userId); + if (needStart) { + Intent intent = new Intent(Intent.ACTION_USER_STARTED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, + false, false, MY_PID, Process.SYSTEM_UID, userId); + } if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) { if (userId != 0) { - intent = new Intent(Intent.ACTION_USER_INITIALIZE); + Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntentLocked(null, null, intent, null, new IIntentReceiver.Stub() { @@ -14116,6 +14212,21 @@ public final class ActivityManagerService extends ActivityManagerNative getUserManagerLocked().userForeground(userId); sendUserSwitchBroadcastsLocked(oldUserId, userId); + if (needStart) { + Intent intent = new Intent(Intent.ACTION_USER_STARTING); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + broadcastIntentLocked(null, null, intent, + null, new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky, int sendingUser) + throws RemoteException { + } + }, 0, null, null, + android.Manifest.permission.INTERACT_ACROSS_USERS, + false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + } } } finally { Binder.restoreCallingIdentity(ident); @@ -14263,7 +14374,8 @@ public final class ActivityManagerService extends ActivityManagerNative num--; continue; } - if (oldUss.mState == UserStartedState.STATE_STOPPING) { + if (oldUss.mState == UserStartedState.STATE_STOPPING + || oldUss.mState == UserStartedState.STATE_SHUTDOWN) { // This user is already stopping, doesn't count. num--; i++; @@ -14328,23 +14440,51 @@ public final class ActivityManagerService extends ActivityManagerNative uss.mStopCallbacks.add(callback); } - if (uss.mState != UserStartedState.STATE_STOPPING) { + if (uss.mState != UserStartedState.STATE_STOPPING + && uss.mState != UserStartedState.STATE_SHUTDOWN) { uss.mState = UserStartedState.STATE_STOPPING; + updateStartedUserArrayLocked(); long ident = Binder.clearCallingIdentity(); try { - // Inform of user switch - Intent intent = new Intent(Intent.ACTION_SHUTDOWN); - final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() { + // We are going to broadcast ACTION_USER_STOPPING and then + // once that is down send a final ACTION_SHUTDOWN and then + // stop the user. + final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING); + stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN); + // This is the result receiver for the final shutdown broadcast. + final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { finishUserStop(uss); } }; - broadcastIntentLocked(null, null, intent, - null, resultReceiver, 0, null, null, null, - true, false, MY_PID, Process.SYSTEM_UID, userId); + // This is the result receiver for the initial stopping broadcast. + final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky, int sendingUser) { + // On to the next. + synchronized (ActivityManagerService.this) { + if (uss.mState != UserStartedState.STATE_STOPPING) { + // Whoops, we are being started back up. Abort, abort! + return; + } + uss.mState = UserStartedState.STATE_SHUTDOWN; + } + broadcastIntentLocked(null, null, shutdownIntent, + null, shutdownReceiver, 0, null, null, null, + true, false, MY_PID, Process.SYSTEM_UID, userId); + } + }; + // Kick things off. + broadcastIntentLocked(null, null, stoppingIntent, + null, stoppingReceiver, 0, null, null, + android.Manifest.permission.INTERACT_ACROSS_USERS, + true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } finally { Binder.restoreCallingIdentity(ident); } @@ -14359,8 +14499,9 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<IStopUserCallback> callbacks; synchronized (this) { callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks); - if (uss.mState != UserStartedState.STATE_STOPPING - || mStartedUsers.get(userId) != uss) { + if (mStartedUsers.get(userId) != uss) { + stopped = false; + } else if (uss.mState != UserStartedState.STATE_SHUTDOWN) { stopped = false; } else { stopped = true; @@ -14407,7 +14548,7 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public boolean isUserRunning(int userId) { + public boolean isUserRunning(int userId, boolean orStopped) { if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: isUserRunning() from pid=" @@ -14418,13 +14559,20 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } synchronized (this) { - return isUserRunningLocked(userId); + return isUserRunningLocked(userId, orStopped); } } - boolean isUserRunningLocked(int userId) { + boolean isUserRunningLocked(int userId, boolean orStopped) { UserStartedState state = mStartedUsers.get(userId); - return state != null && state.mState != UserStartedState.STATE_STOPPING; + if (state == null) { + return false; + } + if (orStopped) { + return true; + } + return state.mState != UserStartedState.STATE_STOPPING + && state.mState != UserStartedState.STATE_SHUTDOWN; } @Override @@ -14444,9 +14592,24 @@ public final class ActivityManagerService extends ActivityManagerNative } private void updateStartedUserArrayLocked() { - mStartedUserArray = new int[mStartedUsers.size()]; + int num = 0; + for (int i=0; i<mStartedUsers.size(); i++) { + UserStartedState uss = mStartedUsers.valueAt(i); + // This list does not include stopping users. + if (uss.mState != UserStartedState.STATE_STOPPING + && uss.mState != UserStartedState.STATE_SHUTDOWN) { + num++; + } + } + mStartedUserArray = new int[num]; + num = 0; for (int i=0; i<mStartedUsers.size(); i++) { - mStartedUserArray[i] = mStartedUsers.keyAt(i); + UserStartedState uss = mStartedUsers.valueAt(i); + if (uss.mState != UserStartedState.STATE_STOPPING + && uss.mState != UserStartedState.STATE_SHUTDOWN) { + mStartedUserArray[num] = mStartedUsers.keyAt(i); + num++; + } } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 6cd86fd..b9f5b5b 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -841,6 +841,7 @@ final class ActivityRecord { } public boolean keyDispatchingTimedOut() { + // TODO: Unify this code with ActivityManagerService.inputDispatchingTimedOut(). ActivityRecord r; ProcessRecord anrApp = null; synchronized(service) { @@ -869,8 +870,7 @@ final class ActivityRecord { } if (anrApp != null) { - service.appNotResponding(anrApp, r, this, - "keyDispatchingTimedOut"); + service.appNotResponding(anrApp, r, this, false, "keyDispatchingTimedOut"); } return true; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 90a7abc..4bcb339 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -420,12 +420,17 @@ final class ActivityStack { mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch"); mLaunchingActivity.setReferenceCounted(false); } - + + private boolean okToShow(ActivityRecord r) { + return r.userId == mCurrentUser + || (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0; + } + final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { int i = mHistory.size()-1; while (i >= 0) { ActivityRecord r = mHistory.get(i); - if (!r.finishing && r != notTop && r.userId == mCurrentUser) { + if (!r.finishing && r != notTop && okToShow(r)) { return r; } i--; @@ -437,7 +442,7 @@ final class ActivityStack { int i = mHistory.size()-1; while (i >= 0) { ActivityRecord r = mHistory.get(i); - if (!r.finishing && !r.delayedResume && r != notTop && r.userId == mCurrentUser) { + if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) { return r; } i--; @@ -460,7 +465,7 @@ final class ActivityStack { ActivityRecord r = mHistory.get(i); // Note: the taskId check depends on real taskId fields being non-zero if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId) - && r.userId == mCurrentUser) { + && okToShow(r)) { return r; } i--; @@ -1806,7 +1811,8 @@ final class ActivityStack { mHistory.add(addPos, r); r.putInHistory(); mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, - r.info.screenOrientation, r.fullscreen); + r.info.screenOrientation, r.fullscreen, + (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0); if (VALIDATE_TOKENS) { validateAppTokensLocked(); } @@ -1870,7 +1876,8 @@ final class ActivityStack { } r.updateOptionsLocked(options); mService.mWindowManager.addAppToken( - addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen); + addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen, + (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0); boolean doShow = true; if (newTask) { // Even though this activity is starting fresh, we still need @@ -1908,7 +1915,8 @@ final class ActivityStack { // If this is the first activity, don't do any fancy animations, // because there is nothing for it to animate on top of. mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, - r.info.screenOrientation, r.fullscreen); + r.info.screenOrientation, r.fullscreen, + (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0); ActivityOptions.abort(options); } if (VALIDATE_TOKENS) { @@ -2436,8 +2444,8 @@ final class ActivityStack { if (err == ActivityManager.START_SUCCESS) { final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0; - Slog.i(TAG, "START {" + intent.toShortString(true, true, true, false) - + " u=" + userId + "} from pid " + (callerApp != null ? callerApp.pid : callingPid)); + Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false) + + "} from pid " + (callerApp != null ? callerApp.pid : callingPid)); } ActivityRecord sourceRecord = null; @@ -2616,7 +2624,6 @@ final class ActivityStack { Bundle options) { final Intent intent = r.intent; final int callingUid = r.launchedFromUid; - final int userId = r.userId; int launchFlags = intent.getFlags(); diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java index 0ebbe3b..ffa1e92 100644 --- a/services/java/com/android/server/am/AppErrorDialog.java +++ b/services/java/com/android/server/am/AppErrorDialog.java @@ -23,12 +23,9 @@ import android.content.DialogInterface; import android.content.res.Resources; import android.os.Handler; import android.os.Message; -import android.util.Slog; import android.view.WindowManager; class AppErrorDialog extends BaseErrorDialog { - private final static String TAG = "AppErrorDialog"; - private final ActivityManagerService mService; private final AppErrorResult mResult; private final ProcessRecord mProc; @@ -76,7 +73,10 @@ class AppErrorDialog extends BaseErrorDialog { setTitle(res.getText(com.android.internal.R.string.aerr_title)); getWindow().addFlags(FLAG_SYSTEM_ERROR); - getWindow().setTitle("Application Error: " + app.info.processName); + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.setTitle("Application Error: " + app.info.processName); + attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + getWindow().setAttributes(attrs); if (app.persistent) { getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); } diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java index b546ae7..af61c9b 100644 --- a/services/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/java/com/android/server/am/AppNotRespondingDialog.java @@ -25,8 +25,8 @@ import android.content.Intent; import android.content.res.Resources; import android.os.Handler; import android.os.Message; -import android.os.Process; import android.util.Slog; +import android.view.WindowManager; class AppNotRespondingDialog extends BaseErrorDialog { private static final String TAG = "AppNotRespondingDialog"; @@ -40,7 +40,7 @@ class AppNotRespondingDialog extends BaseErrorDialog { private final ProcessRecord mProc; public AppNotRespondingDialog(ActivityManagerService service, Context context, - ProcessRecord app, ActivityRecord activity) { + ProcessRecord app, ActivityRecord activity, boolean aboveSystem) { super(context); mService = service; @@ -91,8 +91,14 @@ class AppNotRespondingDialog extends BaseErrorDialog { } setTitle(res.getText(com.android.internal.R.string.anr_title)); + if (aboveSystem) { + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); + } getWindow().addFlags(FLAG_SYSTEM_ERROR); - getWindow().setTitle("Application Not Responding: " + app.info.processName); + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.setTitle("Application Not Responding: " + app.info.processName); + attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + getWindow().setAttributes(attrs); } public void onStop() { diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java index 9fb48b3..d08bb10 100644 --- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java +++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Handler; import android.os.Message; +import android.view.WindowManager; class AppWaitingForDebuggerDialog extends BaseErrorDialog { final ActivityManagerService mService; @@ -52,7 +53,9 @@ class AppWaitingForDebuggerDialog extends BaseErrorDialog { setMessage(text.toString()); setButton(DialogInterface.BUTTON_POSITIVE, "Force Close", mHandler.obtainMessage(1, app)); setTitle("Waiting For Debugger"); - getWindow().setTitle("Waiting For Debugger: " + app.info.processName); + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.setTitle("Waiting For Debugger: " + app.info.processName); + getWindow().setAttributes(attrs); } public void onStop() { diff --git a/services/java/com/android/server/am/BaseErrorDialog.java b/services/java/com/android/server/am/BaseErrorDialog.java index d1e89bc..6ede8f8 100644 --- a/services/java/com/android/server/am/BaseErrorDialog.java +++ b/services/java/com/android/server/am/BaseErrorDialog.java @@ -33,7 +33,9 @@ class BaseErrorDialog extends AlertDialog { getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); - getWindow().setTitle("Error Dialog"); + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.setTitle("Error Dialog"); + getWindow().setAttributes(attrs); setIconAttribute(R.attr.alertDialogIcon); } diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 9f27994..f9630ae 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -54,6 +54,7 @@ public class BroadcastQueue { static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU; static final int MAX_BROADCAST_HISTORY = 25; + static final int MAX_BROADCAST_SUMMARY_HISTORY = 100; final ActivityManagerService mService; @@ -93,6 +94,12 @@ public class BroadcastQueue { = new BroadcastRecord[MAX_BROADCAST_HISTORY]; /** + * Summary of historical data of past broadcasts, for debugging. + */ + final Intent[] mBroadcastSummaryHistory + = new Intent[MAX_BROADCAST_SUMMARY_HISTORY]; + + /** * Set when we current have a BROADCAST_INTENT_MSG in flight. */ boolean mBroadcastsScheduled = false; @@ -151,7 +158,7 @@ public class BroadcastQueue { @Override public void run() { - mService.appNotResponding(mApp, null, null, mAnnotation); + mService.appNotResponding(mApp, null, null, false, mAnnotation); } } @@ -922,6 +929,9 @@ public class BroadcastQueue { MAX_BROADCAST_HISTORY-1); r.finishTime = SystemClock.uptimeMillis(); mBroadcastHistory[0] = r; + System.arraycopy(mBroadcastSummaryHistory, 0, mBroadcastSummaryHistory, 1, + MAX_BROADCAST_SUMMARY_HISTORY-1); + mBroadcastSummaryHistory[0] = r.intent; } final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) { @@ -1006,8 +1016,9 @@ public class BroadcastQueue { } } + int i; boolean printed = false; - for (int i=0; i<MAX_BROADCAST_HISTORY; i++) { + for (i=0; i<MAX_BROADCAST_HISTORY; i++) { BroadcastRecord r = mBroadcastHistory[i]; if (r == null) { break; @@ -1028,11 +1039,44 @@ public class BroadcastQueue { pw.print(i); pw.println(":"); r.dump(pw, " "); } else { - if (i >= 50) { + pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); + pw.print(" "); + pw.println(r.intent.toShortString(false, true, true, false)); + Bundle bundle = r.intent.getExtras(); + if (bundle != null) { + pw.print(" extras: "); pw.println(bundle.toString()); + } + } + } + + if (dumpPackage == null) { + if (dumpAll) { + i = 0; + printed = false; + } + for (; i<MAX_BROADCAST_SUMMARY_HISTORY; i++) { + Intent intent = mBroadcastSummaryHistory[i]; + if (intent == null) { + break; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Historical broadcasts summary [" + mQueueName + "]:"); + printed = true; + } + if (!dumpAll && i >= 50) { pw.println(" ..."); break; } - pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); + pw.print(" #"); pw.print(i); pw.print(": "); + pw.println(intent.toShortString(false, true, true, false)); + Bundle bundle = intent.getExtras(); + if (bundle != null) { + pw.print(" extras: "); pw.println(bundle.toString()); + } } } diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index 85ec328..1cf5b9c 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -81,12 +81,10 @@ class BroadcastRecord extends Binder { final long now = SystemClock.uptimeMillis(); pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId); - pw.print(prefix); pw.println(intent); - if (sticky) { - Bundle bundle = intent.getExtras(); - if (bundle != null) { - pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString()); - } + pw.print(prefix); pw.println(intent.toInsecureString()); + Bundle bundle = intent.getExtras(); + if (bundle != null) { + pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString()); } pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.print(" "); pw.print(callerApp != null ? callerApp.toShortString() : "null"); diff --git a/services/java/com/android/server/am/FactoryErrorDialog.java b/services/java/com/android/server/am/FactoryErrorDialog.java index b19bb5c..0ffb588 100644 --- a/services/java/com/android/server/am/FactoryErrorDialog.java +++ b/services/java/com/android/server/am/FactoryErrorDialog.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Handler; import android.os.Message; +import android.view.WindowManager; class FactoryErrorDialog extends BaseErrorDialog { public FactoryErrorDialog(Context context, CharSequence msg) { @@ -30,7 +31,9 @@ class FactoryErrorDialog extends BaseErrorDialog { setButton(DialogInterface.BUTTON_POSITIVE, context.getText(com.android.internal.R.string.factorytest_reboot), mHandler.obtainMessage(0)); - getWindow().setTitle("Factory Error"); + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.setTitle("Factory Error"); + getWindow().setAttributes(attrs); } public void onStop() { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 652fdb5..7fbab04 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -407,7 +407,7 @@ class ProcessRecord { sb.append('u'); sb.append(userId); sb.append('a'); - sb.append(info.uid%Process.FIRST_APPLICATION_UID); + sb.append(UserHandle.getAppId(info.uid)); 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/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java index 50c8553..0e71f81 100644 --- a/services/java/com/android/server/am/UserStartedState.java +++ b/services/java/com/android/server/am/UserStartedState.java @@ -23,9 +23,14 @@ import android.app.IStopUserCallback; import android.os.UserHandle; public class UserStartedState { + // User is first coming up. public final static int STATE_BOOTING = 0; + // User is in the normal running state. public final static int STATE_RUNNING = 1; + // User is in the initial process of being stopped. public final static int STATE_STOPPING = 2; + // User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN. + public final static int STATE_SHUTDOWN = 3; public final UserHandle mHandle; public final ArrayList<IStopUserCallback> mStopCallbacks @@ -40,7 +45,14 @@ public class UserStartedState { } void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.print("mState="); pw.print(mState); + pw.print(prefix); pw.print("mState="); + switch (mState) { + case STATE_BOOTING: pw.print("BOOTING"); break; + case STATE_RUNNING: pw.print("RUNNING"); break; + case STATE_STOPPING: pw.print("STOPPING"); break; + case STATE_SHUTDOWN: pw.print("SHUTDOWN"); break; + default: pw.print(mState); break; + } if (switching) pw.print(" SWITCHING"); if (initializing) pw.print(" INITIALIZING"); pw.println(); diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index b38d617..e4a7ead 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -454,7 +454,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if (mTetheredNotification.icon == icon) { return; } - notificationManager.cancel(mTetheredNotification.icon); + notificationManager.cancelAsUser(null, mTetheredNotification.icon, + UserHandle.ALL); } Intent intent = new Intent(); diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java index f5aa3d4..a3ab3c1 100644 --- a/services/java/com/android/server/display/DisplayDevice.java +++ b/services/java/com/android/server/display/DisplayDevice.java @@ -105,6 +105,18 @@ abstract class DisplayDevice { } /** + * Blanks the display, if supported. + */ + public void blankLocked() { + } + + /** + * Unblanks the display, if supported. + */ + public void unblankLocked() { + } + + /** * Sets the display layer stack while in a transaction. */ public final void setLayerStackInTransactionLocked(int layerStack) { diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index b8c6cd5..0a42528 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -103,6 +103,10 @@ public final class DisplayManagerService extends IDisplayManager.Stub { private static final int MSG_REQUEST_TRAVERSAL = 4; private static final int MSG_UPDATE_VIEWPORT = 5; + private static final int DISPLAY_BLANK_STATE_UNKNOWN = 0; + private static final int DISPLAY_BLANK_STATE_BLANKED = 1; + private static final int DISPLAY_BLANK_STATE_UNBLANKED = 2; + private final Context mContext; private final boolean mHeadless; private final DisplayManagerHandler mHandler; @@ -141,6 +145,9 @@ public final class DisplayManagerService extends IDisplayManager.Stub { new SparseArray<LogicalDisplay>(); private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; + // Set to true if all displays have been blanked by the power manager. + private int mAllDisplayBlankStateFromPowerManager; + // Set to true when there are pending display changes that have yet to be applied // to the surface flinger state. private boolean mPendingTraversal; @@ -286,6 +293,40 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } /** + * Called by the power manager to blank all displays. + */ + public void blankAllDisplaysFromPowerManager() { + synchronized (mSyncRoot) { + if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_BLANKED) { + mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_BLANKED; + + final int count = mDisplayDevices.size(); + for (int i = 0; i < count; i++) { + DisplayDevice device = mDisplayDevices.get(i); + device.blankLocked(); + } + } + } + } + + /** + * Called by the power manager to unblank all displays. + */ + public void unblankAllDisplaysFromPowerManager() { + synchronized (mSyncRoot) { + if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_UNBLANKED) { + mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNBLANKED; + + final int count = mDisplayDevices.size(); + for (int i = 0; i < count; i++) { + DisplayDevice device = mDisplayDevices.get(i); + device.unblankLocked(); + } + } + } + } + + /** * Returns information about the specified logical display. * * @param displayId The logical display id. @@ -528,6 +569,17 @@ public final class DisplayManagerService extends IDisplayManager.Stub { mDisplayDevices.add(device); addLogicalDisplayLocked(device); scheduleTraversalLocked(false); + + // Blank or unblank the display immediately to match the state requested + // by the power manager (if known). + switch (mAllDisplayBlankStateFromPowerManager) { + case DISPLAY_BLANK_STATE_BLANKED: + device.blankLocked(); + break; + case DISPLAY_BLANK_STATE_UNBLANKED: + device.unblankLocked(); + break; + } } } @@ -788,9 +840,18 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } pw.println("DISPLAY MANAGER (dumpsys display)"); - pw.println(" mHeadless=" + mHeadless); synchronized (mSyncRoot) { + pw.println(" mHeadless=" + mHeadless); + pw.println(" mOnlyCode=" + mOnlyCore); + pw.println(" mSafeMode=" + mSafeMode); + pw.println(" mPendingTraversal=" + mPendingTraversal); + pw.println(" mAllDisplayBlankStateFromPowerManager=" + + mAllDisplayBlankStateFromPowerManager); + pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); + pw.println(" mDefaultViewport=" + mDefaultViewport); + pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); @@ -817,10 +878,6 @@ public final class DisplayManagerService extends IDisplayManager.Stub { pw.println(" Display " + displayId + ":"); display.dumpLocked(ipw); } - - pw.println(); - pw.println("Default viewport: " + mDefaultViewport); - pw.println("External touch viewport: " + mExternalTouchViewport); } } diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java index 679a67e..d780006 100644 --- a/services/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/java/com/android/server/display/LocalDisplayAdapter.java @@ -92,6 +92,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayDeviceInfo mInfo; private boolean mHavePendingChanges; + private boolean mBlanked; public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, PhysicalDisplayInfo phys) { @@ -150,10 +151,23 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override + public void blankLocked() { + mBlanked = true; + Surface.blankDisplay(getDisplayTokenLocked()); + } + + @Override + public void unblankLocked() { + mBlanked = false; + Surface.unblankDisplay(getDisplayTokenLocked()); + } + + @Override public void dumpLocked(PrintWriter pw) { super.dumpLocked(pw); pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); pw.println("mPhys=" + mPhys); + pw.println("mBlanked=" + mBlanked); } } diff --git a/services/java/com/android/server/dreams/DreamController.java b/services/java/com/android/server/dreams/DreamController.java index 6db495a..bfb60bb 100644 --- a/services/java/com/android/server/dreams/DreamController.java +++ b/services/java/com/android/server/dreams/DreamController.java @@ -132,8 +132,15 @@ final class DreamController { } if (oldDream.mService != null) { - // TODO: It would be nice to tell the dream that it's being stopped so that - // it can shut down nicely before we yank its window token out from under it. + // Tell the dream that it's being stopped so that + // it can shut down nicely before we yank its window token out from + // under it. + try { + oldDream.mService.detach(); + } catch (RemoteException ex) { + // we don't care; this thing is on the way out + } + try { oldDream.mService.asBinder().unlinkToDeath(oldDream, 0); } catch (NoSuchElementException ex) { diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java index 7d030e9..f5cc59f 100644 --- a/services/java/com/android/server/location/GeocoderProxy.java +++ b/services/java/com/android/server/location/GeocoderProxy.java @@ -21,6 +21,7 @@ import android.location.Address; import android.location.GeocoderParams; import android.location.IGeocodeProvider; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import com.android.server.ServiceWatcher; @@ -38,8 +39,8 @@ public class GeocoderProxy { private final ServiceWatcher mServiceWatcher; public static GeocoderProxy createAndBind(Context context, - List<String> initialPackageNames) { - GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames); + List<String> initialPackageNames, int userId) { + GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, userId); if (proxy.bind()) { return proxy; } else { @@ -47,11 +48,11 @@ public class GeocoderProxy { } } - public GeocoderProxy(Context context, List<String> initialPackageNames) { + public GeocoderProxy(Context context, List<String> initialPackageNames, int userId) { mContext = context; mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames, - null, null); + null, null, userId); } private boolean bind () { diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java index 26d9c15..d04d2f3 100644 --- a/services/java/com/android/server/location/GeofenceManager.java +++ b/services/java/com/android/server/location/GeofenceManager.java @@ -58,7 +58,6 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish private Object mLock = new Object(); // access to members below is synchronized on mLock - private Location mLastLocation; private List<GeofenceState> mFences = new LinkedList<GeofenceState>(); public GeofenceManager(Context context, LocationBlacklist blacklist) { @@ -77,7 +76,8 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, int uid, String packageName) { - GeofenceState state = new GeofenceState(geofence, mLastLocation, + Location lastLocation = mLocationManager.getLastLocation(); + GeofenceState state = new GeofenceState(geofence, lastLocation, request.getExpireAt(), packageName, intent); synchronized (mLock) { @@ -146,8 +146,6 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish List<PendingIntent> exitIntents = new LinkedList<PendingIntent>(); synchronized (mLock) { - mLastLocation = location; - removeExpiredFencesLocked(); for (GeofenceState state : mFences) { diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index a254d74..c272da4 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -783,6 +783,11 @@ public class GpsLocationProvider implements LocationProviderInterface { sendMessage(SET_REQUEST, 0, new GpsRequest(request, source)); } + @Override + public void switchUser(int userId) { + // nothing to do here + } + private void handleSetRequest(ProviderRequest request, WorkSource source) { if (DEBUG) Log.d(TAG, "setRequest " + request); diff --git a/services/java/com/android/server/location/LocationBlacklist.java b/services/java/com/android/server/location/LocationBlacklist.java index 6ad1a92..2437a37 100644 --- a/services/java/com/android/server/location/LocationBlacklist.java +++ b/services/java/com/android/server/location/LocationBlacklist.java @@ -20,6 +20,7 @@ package com.android.server.location; import android.content.Context; import android.database.ContentObserver; import android.os.Handler; +import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import android.util.Slog; @@ -48,6 +49,8 @@ public final class LocationBlacklist extends ContentObserver { // all fields below synchronized on mLock private String[] mWhitelist = new String[0]; private String[] mBlacklist = new String[0]; + + private int mCurrentUserId = UserHandle.USER_OWNER; public LocationBlacklist(Context context, Handler handler) { super(handler); @@ -56,20 +59,22 @@ public final class LocationBlacklist extends ContentObserver { public void init() { mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( - BLACKLIST_CONFIG_NAME), false, this); + BLACKLIST_CONFIG_NAME), false, this, UserHandle.USER_ALL); // mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( -// WHITELIST_CONFIG_NAME), false, this); +// WHITELIST_CONFIG_NAME), false, this, UserHandle.USER_ALL); reloadBlacklist(); } + private void reloadBlacklistLocked() { + mWhitelist = getStringArrayLocked(WHITELIST_CONFIG_NAME); + Slog.i(TAG, "whitelist: " + Arrays.toString(mWhitelist)); + mBlacklist = getStringArrayLocked(BLACKLIST_CONFIG_NAME); + Slog.i(TAG, "blacklist: " + Arrays.toString(mBlacklist)); + } + private void reloadBlacklist() { - String blacklist[] = getStringArray(BLACKLIST_CONFIG_NAME); - String whitelist[] = getStringArray(WHITELIST_CONFIG_NAME); synchronized (mLock) { - mWhitelist = whitelist; - Slog.i(TAG, "whitelist: " + Arrays.toString(mWhitelist)); - mBlacklist = blacklist; - Slog.i(TAG, "blacklist: " + Arrays.toString(mBlacklist)); + reloadBlacklistLocked(); } } @@ -78,7 +83,6 @@ public final class LocationBlacklist extends ContentObserver { * (package name matches blacklist, and does not match whitelist) */ public boolean isBlacklisted(String packageName) { - /* synchronized (mLock) { for (String black : mBlacklist) { if (packageName.startsWith(black)) { @@ -92,7 +96,6 @@ public final class LocationBlacklist extends ContentObserver { } } } - */ return false; } @@ -113,8 +116,19 @@ public final class LocationBlacklist extends ContentObserver { reloadBlacklist(); } - private String[] getStringArray(String key) { - String flatString = Settings.Secure.getString(mContext.getContentResolver(), key); + public void switchUser(int userId) { + synchronized(mLock) { + mCurrentUserId = userId; + reloadBlacklistLocked(); + } + } + + private String[] getStringArrayLocked(String key) { + String flatString; + synchronized(mLock) { + flatString = Settings.Secure.getStringForUser(mContext.getContentResolver(), key, + mCurrentUserId); + } if (flatString == null) { return new String[0]; } diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java index 6f09232..80e71f1 100644 --- a/services/java/com/android/server/location/LocationProviderInterface.java +++ b/services/java/com/android/server/location/LocationProviderInterface.java @@ -38,6 +38,8 @@ public interface LocationProviderInterface { public boolean isEnabled(); public void setRequest(ProviderRequest request, WorkSource source); + public void switchUser(int userId); + public void dump(FileDescriptor fd, PrintWriter pw, String[] args); // --- deprecated (but still supported) --- diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java index 7faf72c..dd2e71c 100644 --- a/services/java/com/android/server/location/LocationProviderProxy.java +++ b/services/java/com/android/server/location/LocationProviderProxy.java @@ -25,6 +25,7 @@ import android.location.LocationProvider; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; import android.os.WorkSource; import android.util.Log; @@ -54,9 +55,9 @@ public class LocationProviderProxy implements LocationProviderInterface { private WorkSource mWorksource = new WorkSource(); public static LocationProviderProxy createAndBind(Context context, String name, String action, - List<String> initialPackageNames, Handler handler) { + List<String> initialPackageNames, Handler handler, int userId) { LocationProviderProxy proxy = new LocationProviderProxy(context, name, action, - initialPackageNames, handler); + initialPackageNames, handler, userId); if (proxy.bind()) { return proxy; } else { @@ -65,11 +66,11 @@ public class LocationProviderProxy implements LocationProviderInterface { } private LocationProviderProxy(Context context, String name, String action, - List<String> initialPackageNames, Handler handler) { + List<String> initialPackageNames, Handler handler, int userId) { mContext = context; mName = name; mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames, - mNewServiceWork, handler); + mNewServiceWork, handler, userId); } private boolean bind () { @@ -211,6 +212,11 @@ public class LocationProviderProxy implements LocationProviderInterface { } @Override + public void switchUser(int userId) { + mServiceWatcher.switchUser(userId); + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.append("REMOTE SERVICE"); pw.append(" name=").append(mName); diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java index 36c43ff..1194cbc 100644 --- a/services/java/com/android/server/location/MockProvider.java +++ b/services/java/com/android/server/location/MockProvider.java @@ -156,6 +156,11 @@ public class MockProvider implements LocationProviderInterface { public void setRequest(ProviderRequest request, WorkSource source) { } @Override + public void switchUser(int userId) { + // nothing to do here + } + + @Override public boolean sendExtraCommand(String command, Bundle extras) { return false; } diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java index 71bae07..734c572 100644 --- a/services/java/com/android/server/location/PassiveProvider.java +++ b/services/java/com/android/server/location/PassiveProvider.java @@ -96,6 +96,11 @@ public class PassiveProvider implements LocationProviderInterface { mReportLocation = request.reportLocation; } + @Override + public void switchUser(int userId) { + // nothing to do here + } + public void updateLocation(Location location) { if (mReportLocation) { try { diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 0f3dc92..b8d7286 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -1300,27 +1300,6 @@ public class PackageManagerService extends IPackageManager.Stub { ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL) : 0)); - // Verify that all of the preferred activity components actually - // exist. It is possible for applications to be updated and at - // that point remove a previously declared activity component that - // had been set as a preferred activity. We try to clean this up - // the next time we encounter that preferred activity, but it is - // possible for the user flow to never be able to return to that - // situation so here we do a sanity check to make sure we haven't - // left any junk around. - ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>(); - for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) { - if (mActivities.mActivities.get(pa.mPref.mComponent) == null) { - removed.add(pa); - } - } - for (int i=0; i<removed.size(); i++) { - PreferredActivity pa = removed.get(i); - Slog.w(TAG, "Removing dangling preferred activity: " - + pa.mPref.mComponent); - mSettings.mPreferredActivities.removeFilter(pa); - } - // can downgrade to reader mSettings.writeLPr(); @@ -2504,9 +2483,11 @@ public class PackageManagerService extends IPackageManager.Stub { intent = intent.getSelector(); } if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); - List<PreferredActivity> prefs = - mSettings.mPreferredActivities.queryIntent(intent, resolvedType, - (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId); + PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); + List<PreferredActivity> prefs = pir != null + ? pir.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId) + : null; if (prefs != null && prefs.size() > 0) { // First figure out how good the original match set is. // We will only allow preferred activities that came @@ -2537,9 +2518,6 @@ public class PackageManagerService extends IPackageManager.Stub { final int M = prefs.size(); for (int i=0; i<M; i++) { final PreferredActivity pa = prefs.get(i); - if (pa.mUserId != userId) { - continue; - } if (pa.mPref.mMatch != match) { continue; } @@ -2560,7 +2538,7 @@ public class PackageManagerService extends IPackageManager.Stub { // it from the preferred activities list, and skip it. Slog.w(TAG, "Removing dangling preferred activity: " + pa.mPref.mComponent); - mSettings.mPreferredActivities.removeFilter(pa); + pir.removeFilter(pa); continue; } for (int j=0; j<N; j++) { @@ -2580,7 +2558,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (!pa.mPref.sameSet(query, priority)) { Slog.i(TAG, "Result set changed, dropping preferred activity for " + intent + " type " + resolvedType); - mSettings.mPreferredActivities.removeFilter(pa); + pir.removeFilter(pa); return null; } @@ -6396,12 +6374,22 @@ public class PackageManagerService extends IPackageManager.Stub { mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { + /* + * ADB installs appear as UserHandle.USER_ALL, and can only be performed by + * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER. + */ + int userIdentifier = getUser().getIdentifier(); + if (userIdentifier == UserHandle.USER_ALL + && ((flags & PackageManager.INSTALL_FROM_ADB) != 0)) { + userIdentifier = UserHandle.USER_OWNER; + } + /* * Determine if we have any installed package verifiers. If we * do, then we'll defer to them to verify the packages. */ final int requiredUid = mRequiredVerifierPackage == null ? -1 - : getPackageUid(mRequiredVerifierPackage, getUser().getIdentifier()); + : getPackageUid(mRequiredVerifierPackage, userIdentifier); if (requiredUid != -1 && isVerificationEnabled(flags)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); @@ -8682,9 +8670,9 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.i(TAG, "Adding preferred activity " + activity + " for user " + userId + " :"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); - mSettings.mPreferredActivities.addFilter( - new PreferredActivity(filter, match, set, activity, userId)); - scheduleWriteSettingsLocked(); + mSettings.editPreferredActivitiesLPw(userId).addFilter( + new PreferredActivity(filter, match, set, activity)); + mSettings.writePackageRestrictionsLPr(userId); } } @@ -8722,25 +8710,27 @@ public class PackageManagerService extends IPackageManager.Stub { final int callingUserId = UserHandle.getCallingUserId(); ArrayList<PreferredActivity> removed = null; - Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); - String action = filter.getAction(0); - String category = filter.getCategory(0); - while (it.hasNext()) { - PreferredActivity pa = it.next(); - if (pa.mUserId != callingUserId) continue; - if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) { - if (removed == null) { - removed = new ArrayList<PreferredActivity>(); + PreferredIntentResolver pir = mSettings.mPreferredActivities.get(callingUserId); + if (pir != null) { + Iterator<PreferredActivity> it = pir.filterIterator(); + String action = filter.getAction(0); + String category = filter.getCategory(0); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) { + if (removed == null) { + removed = new ArrayList<PreferredActivity>(); + } + removed.add(pa); + Log.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - removed.add(pa); - Log.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":"); - filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - } - if (removed != null) { - for (int i=0; i<removed.size(); i++) { - PreferredActivity pa = removed.get(i); - mSettings.mPreferredActivities.removeFilter(pa); + if (removed != null) { + for (int i=0; i<removed.size(); i++) { + PreferredActivity pa = removed.get(i); + pir.removeFilter(pa); + } } } addPreferredActivity(filter, match, set, activity, callingUserId); @@ -8776,27 +8766,33 @@ public class PackageManagerService extends IPackageManager.Stub { /** This method takes a specific user id as well as UserHandle.USER_ALL. */ boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) { ArrayList<PreferredActivity> removed = null; - Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); - while (it.hasNext()) { - PreferredActivity pa = it.next(); - if (userId != UserHandle.USER_ALL && pa.mUserId != userId) { + boolean changed = false; + for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { + final int thisUserId = mSettings.mPreferredActivities.keyAt(i); + PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i); + if (userId != UserHandle.USER_ALL && userId != thisUserId) { continue; } - if (pa.mPref.mComponent.getPackageName().equals(packageName)) { - if (removed == null) { - removed = new ArrayList<PreferredActivity>(); + Iterator<PreferredActivity> it = pir.filterIterator(); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + if (pa.mPref.mComponent.getPackageName().equals(packageName)) { + if (removed == null) { + removed = new ArrayList<PreferredActivity>(); + } + removed.add(pa); } - removed.add(pa); } - } - if (removed != null) { - for (int i=0; i<removed.size(); i++) { - PreferredActivity pa = removed.get(i); - mSettings.mPreferredActivities.removeFilter(pa); + if (removed != null) { + for (int j=0; j<removed.size(); j++) { + PreferredActivity pa = removed.get(j); + pir.removeFilter(pa); + } + changed = true; + mSettings.writePackageRestrictionsLPr(thisUserId); } - return true; } - return false; + return changed; } public int getPreferredActivities(List<IntentFilter> outFilters, @@ -8806,19 +8802,19 @@ public class PackageManagerService extends IPackageManager.Stub { final int userId = UserHandle.getCallingUserId(); // reader synchronized (mPackages) { - final Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); - while (it.hasNext()) { - final PreferredActivity pa = it.next(); - if (pa.mUserId != userId) { - continue; - } - if (packageName == null - || pa.mPref.mComponent.getPackageName().equals(packageName)) { - if (outFilters != null) { - outFilters.add(new IntentFilter(pa)); - } - if (outActivities != null) { - outActivities.add(pa.mPref.mComponent); + PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); + if (pir != null) { + final Iterator<PreferredActivity> it = pir.filterIterator(); + while (it.hasNext()) { + final PreferredActivity pa = it.next(); + if (packageName == null + || pa.mPref.mComponent.getPackageName().equals(packageName)) { + if (outFilters != null) { + outFilters.add(new IntentFilter(pa)); + } + if (outActivities != null) { + outActivities.add(pa.mPref.mComponent); + } } } } @@ -9041,6 +9037,39 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_SETTINGS) { Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled); } + + synchronized (mPackages) { + // Verify that all of the preferred activity components actually + // exist. It is possible for applications to be updated and at + // that point remove a previously declared activity component that + // had been set as a preferred activity. We try to clean this up + // the next time we encounter that preferred activity, but it is + // possible for the user flow to never be able to return to that + // situation so here we do a sanity check to make sure we haven't + // left any junk around. + ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>(); + for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { + PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i); + removed.clear(); + for (PreferredActivity pa : pir.filterSet()) { + if (mActivities.mActivities.get(pa.mPref.mComponent) == null) { + removed.add(pa); + } + } + if (removed.size() > 0) { + for (int j=0; j<removed.size(); j++) { + PreferredActivity pa = removed.get(i); + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.w(TAG, "Removing dangling preferred activity: " + + pa.mPref.mComponent, here); + pir.removeFilter(pa); + } + mSettings.writePackageRestrictionsLPr( + mSettings.mPreferredActivities.keyAt(i)); + } + } + } } public boolean isSafeMode() { @@ -9281,11 +9310,16 @@ public class PackageManagerService extends IPackageManager.Stub { } if (dumpState.isDumping(DumpState.DUMP_PREFERRED)) { - if (mSettings.mPreferredActivities.dump(pw, - dumpState.getTitlePrinted() ? "\nPreferred Activities:" - : "Preferred Activities:", " ", - packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { - dumpState.setTitlePrinted(true); + for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { + PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i); + int user = mSettings.mPreferredActivities.keyAt(i); + if (pir.dump(pw, + dumpState.getTitlePrinted() + ? "\nPreferred Activities User " + user + ":" + : "Preferred Activities User " + user + ":", " ", + packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { + dumpState.setTitlePrinted(true); + } } } @@ -9299,7 +9333,7 @@ public class PackageManagerService extends IPackageManager.Stub { serializer.startDocument(null, true); serializer.setFeature( "http://xmlpull.org/v1/doc/features.html#indent-output", true); - mSettings.writePreferredActivitiesLPr(serializer); + mSettings.writePreferredActivitiesLPr(serializer, 0); serializer.endDocument(); serializer.flush(); } catch (IllegalArgumentException e) { @@ -10045,11 +10079,6 @@ public class PackageManagerService extends IPackageManager.Stub { /** Called by UserManagerService */ void cleanUpUserLILPw(int userHandle) { - // Disable all the packages for the user first - Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet(); - for (Entry<String, PackageSetting> entry : entries) { - entry.getValue().removeUser(userHandle); - } if (mDirtyUsers.remove(userHandle)); mSettings.removeUserLPr(userHandle); if (mInstaller != null) { @@ -10063,17 +10092,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** Called by UserManagerService */ void createNewUserLILPw(int userHandle, File path) { if (mInstaller != null) { - path.mkdir(); - FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG - | FileUtils.S_IXOTH, -1, -1); - for (PackageSetting ps : mSettings.mPackages.values()) { - // Only system apps are initially installed. - ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle); - // Need to create a data directory for all apps under this user. - mInstaller.createUserData(ps.name, - UserHandle.getUid(userHandle, ps.appId), userHandle); - } - mSettings.writePackageRestrictionsLPr(userHandle); + mSettings.createNewUserLILPw(mInstaller, userHandle, path); } } diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java index 5539e84..dbf56ef 100644 --- a/services/java/com/android/server/pm/PreferredActivity.java +++ b/services/java/com/android/server/pm/PreferredActivity.java @@ -36,32 +36,17 @@ class PreferredActivity extends IntentFilter implements PreferredComponent.Callb static final String ATTR_USER_ID = "userId"; final PreferredComponent mPref; - final int mUserId; PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { - this(filter, match, set, activity, 0); - } - - PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, - int userId) { super(filter); - mUserId = userId; mPref = new PreferredComponent(this, match, set, activity); } PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException { - String userIdString = parser.getAttributeValue(null, ATTR_USER_ID); - if (userIdString != null && userIdString.length() > 0) { - mUserId = Integer.parseInt(userIdString); - } else { - // Old format with no userId specified - assume primary user - mUserId = 0; - } mPref = new PreferredComponent(this, parser); } public void writeToXml(XmlSerializer serializer) throws IOException { - serializer.attribute(null, ATTR_USER_ID, Integer.toString(mUserId)); mPref.writeToXml(serializer); serializer.startTag(null, "filter"); super.writeToXml(serializer); diff --git a/services/java/com/android/server/pm/PreferredIntentResolver.java b/services/java/com/android/server/pm/PreferredIntentResolver.java new file mode 100644 index 0000000..3f1e50c --- /dev/null +++ b/services/java/com/android/server/pm/PreferredIntentResolver.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 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.server.pm; + +import java.io.PrintWriter; + +import com.android.server.IntentResolver; + +public class PreferredIntentResolver + extends IntentResolver<PreferredActivity, PreferredActivity> { + @Override + protected PreferredActivity[] newArray(int size) { + return new PreferredActivity[size]; + } + @Override + protected String packageForFilter(PreferredActivity filter) { + return filter.mPref.mComponent.getPackageName(); + } + @Override + protected void dumpFilter(PrintWriter out, String prefix, + PreferredActivity filter) { + filter.mPref.dump(out, prefix, filter); + } +} diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index bdf5044..3a54514 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -70,6 +70,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; import libcore.io.IoUtils; @@ -123,22 +125,9 @@ final class Settings { // The user's preferred activities associated with particular intent // filters. - final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = - new IntentResolver<PreferredActivity, PreferredActivity>() { - @Override - protected PreferredActivity[] newArray(int size) { - return new PreferredActivity[size]; - } - @Override - protected String packageForFilter(PreferredActivity filter) { - return filter.mPref.mComponent.getPackageName(); - } - @Override - protected void dumpFilter(PrintWriter out, String prefix, - PreferredActivity filter) { - filter.mPref.dump(out, prefix, filter); - } - }; + final SparseArray<PreferredIntentResolver> mPreferredActivities = + new SparseArray<PreferredIntentResolver>(); + final HashMap<String, SharedUserSetting> mSharedUsers = new HashMap<String, SharedUserSetting>(); private final ArrayList<Object> mUserIds = new ArrayList<Object>(); @@ -745,6 +734,15 @@ final class Settings { } } + PreferredIntentResolver editPreferredActivitiesLPw(int userId) { + PreferredIntentResolver pir = mPreferredActivities.get(userId); + if (pir == null) { + pir = new PreferredIntentResolver(); + mPreferredActivities.put(userId, pir); + } + return pir; + } + private File getUserPackagesStateFile(int userId) { return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml"); } @@ -775,6 +773,35 @@ final class Settings { } } + private void readPreferredActivitiesLPw(XmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals(TAG_ITEM)) { + PreferredActivity pa = new PreferredActivity(parser); + if (pa.mPref.getParseError() == null) { + editPreferredActivitiesLPw(userId).addFilter(pa); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <preferred-activity> " + + pa.mPref.getParseError() + " at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <preferred-activities>: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + void readPackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Reading package restrictions for user=" + userId); @@ -893,6 +920,8 @@ final class Settings { ps.setUserState(userId, enabled, installed, stopped, notLaunched, enabledComponents, disabledComponents); + } else if (tagName.equals("preferred-activities")) { + readPreferredActivitiesLPw(parser, userId); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: " + parser.getName()); @@ -942,6 +971,20 @@ final class Settings { return components; } + void writePreferredActivitiesLPr(XmlSerializer serializer, int userId) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, "preferred-activities"); + PreferredIntentResolver pir = mPreferredActivities.get(userId); + if (pir != null) { + for (final PreferredActivity pa : pir.filterSet()) { + serializer.startTag(null, TAG_ITEM); + pa.writeToXml(serializer); + serializer.endTag(null, TAG_ITEM); + } + } + serializer.endTag(null, "preferred-activities"); + } + void writePackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Writing package restrictions for user=" + userId); @@ -1028,6 +1071,8 @@ final class Settings { } } + writePreferredActivitiesLPr(serializer, userId); + serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS); serializer.endDocument(); @@ -1237,8 +1282,6 @@ final class Settings { writeDisabledSysPackageLPr(serializer, pkg); } - writePreferredActivitiesLPr(serializer); - for (final SharedUserSetting usr : mSharedUsers.values()) { serializer.startTag(null, "shared-user"); serializer.attribute(null, ATTR_NAME, usr.name); @@ -1366,17 +1409,6 @@ final class Settings { //Debug.stopMethodTracing(); } - void writePreferredActivitiesLPr(XmlSerializer serializer) - throws IllegalArgumentException, IllegalStateException, IOException { - serializer.startTag(null, "preferred-activities"); - for (final PreferredActivity pa : mPreferredActivities.filterSet()) { - serializer.startTag(null, TAG_ITEM); - pa.writeToXml(serializer); - serializer.endTag(null, TAG_ITEM); - } - serializer.endTag(null, "preferred-activities"); - } - void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg) throws java.io.IOException { serializer.startTag(null, "updated-package"); @@ -1554,7 +1586,7 @@ final class Settings { mReadMessages.append("No settings file found\n"); PackageManagerService.reportSettingsProblem(Log.INFO, "No settings file; creating initial state"); - readDefaultPreferredAppsLPw(); + readDefaultPreferredAppsLPw(0); return false; } str = new FileInputStream(mSettingsFilename); @@ -1596,7 +1628,9 @@ final class Settings { } else if (tagName.equals("preferred-packages")) { // no longer used. } else if (tagName.equals("preferred-activities")) { - readPreferredActivitiesLPw(parser); + // Upgrading from old single-user implementation; + // these are the preferred activities for user 0. + readPreferredActivitiesLPw(parser, 0); } else if (tagName.equals("updated-package")) { readDisabledSysPackageLPw(parser); } else if (tagName.equals("cleaning-package")) { @@ -1733,7 +1767,7 @@ final class Settings { return true; } - private void readDefaultPreferredAppsLPw() { + private void readDefaultPreferredAppsLPw(int userId) { // Read preferred apps from .../etc/preferred-apps directory. File preferredDir = new File(Environment.getRootDirectory(), "etc/preferred-apps"); if (!preferredDir.exists() || !preferredDir.isDirectory()) { @@ -1776,7 +1810,7 @@ final class Settings { + " does not start with 'preferred-activities'"); continue; } - readPreferredActivitiesLPw(parser); + readPreferredActivitiesLPw(parser, userId); } catch (XmlPullParserException e) { Slog.w(TAG, "Error reading apps file " + f, e); } catch (IOException e) { @@ -2291,36 +2325,27 @@ final class Settings { } } - private void readPreferredActivitiesLPw(XmlPullParser parser) throws XmlPullParserException, - IOException { - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals(TAG_ITEM)) { - PreferredActivity pa = new PreferredActivity(parser); - if (pa.mPref.getParseError() == null) { - mPreferredActivities.addFilter(pa); - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: <preferred-activity> " - + pa.mPref.getParseError() + " at " - + parser.getPositionDescription()); - } - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element under <preferred-activities>: " + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } + void createNewUserLILPw(Installer installer, int userHandle, File path) { + path.mkdir(); + FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG + | FileUtils.S_IXOTH, -1, -1); + for (PackageSetting ps : mPackages.values()) { + // Only system apps are initially installed. + ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle); + // Need to create a data directory for all apps under this user. + installer.createUserData(ps.name, + UserHandle.getUid(userHandle, ps.appId), userHandle); } + readDefaultPreferredAppsLPw(userHandle); + writePackageRestrictionsLPr(userHandle); } void removeUserLPr(int userId) { + Set<Entry<String, PackageSetting>> entries = mPackages.entrySet(); + for (Entry<String, PackageSetting> entry : entries) { + entry.getValue().removeUser(userId); + } + mPreferredActivities.remove(userId); File file = getUserPackagesStateFile(userId); file.delete(); file = getUserPackagesStateBackupFile(userId); diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index a0326c5..4f9375a 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -78,6 +78,8 @@ public class UserManagerService extends IUserManager.Stub { private static final String USER_LIST_FILENAME = "userlist.xml"; private static final String USER_PHOTO_FILENAME = "photo.png"; + private static final int MIN_USER_ID = 10; + private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms private final Context mContext; @@ -459,6 +461,7 @@ public class UserManagerService extends IUserManager.Stub { UserInfo primary = new UserInfo(0, "Primary", null, UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); mUsers.put(0, primary); + mNextSerialNumber = MIN_USER_ID; updateUserIdsLocked(); writeUserListLocked(); @@ -832,7 +835,7 @@ public class UserManagerService extends IUserManager.Stub { */ private int getNextAvailableIdLocked() { synchronized (mPackagesLock) { - int i = 10; + int i = MIN_USER_ID; while (i < Integer.MAX_VALUE) { if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) { break; @@ -862,7 +865,7 @@ public class UserManagerService extends IUserManager.Stub { for (int i = 0; i < mUsers.size(); i++) { UserInfo user = mUsers.valueAt(i); if (user == null) continue; - pw.print(" "); pw.print(user); + pw.print(" "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber); if (mRemovingUserIds.contains(mUsers.keyAt(i))) pw.print(" <removing> "); if (user.partial) pw.print(" <partial>"); pw.println(); diff --git a/services/java/com/android/server/power/DisplayBlanker.java b/services/java/com/android/server/power/DisplayBlanker.java new file mode 100644 index 0000000..6072053 --- /dev/null +++ b/services/java/com/android/server/power/DisplayBlanker.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 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.server.power; + +/** + * Blanks or unblanks all displays. + */ +interface DisplayBlanker { + public void blankAllDisplays(); + public void unblankAllDisplays(); +} diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index 4f8cdde..6a57372 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -89,6 +89,9 @@ final class DisplayPowerController { // auto-brightness adjustment setting. private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f; + // The minimum reduction in brightness when dimmed. + private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10; + // If true, enables the use of the current time as an auto-brightness adjustment. // The basic idea here is to expand the dynamic range of auto-brightness // when it is especially dark outside. The light sensor tends to perform @@ -117,8 +120,9 @@ final class DisplayPowerController { private static final int PROXIMITY_NEGATIVE = 0; private static final int PROXIMITY_POSITIVE = 1; - // Proximity sensor debounce delay in milliseconds. - private static final int PROXIMITY_SENSOR_DEBOUNCE_DELAY = 250; + // Proximity sensor debounce delay in milliseconds for positive or negative transitions. + private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0; + private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 500; // Trigger proximity if distance is less than 5 cm. private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; @@ -155,6 +159,9 @@ final class DisplayPowerController { // A suspend blocker. private final SuspendBlocker mSuspendBlocker; + // The display blanker. + private final DisplayBlanker mDisplayBlanker; + // Our handler. private final DisplayControllerHandler mHandler; @@ -184,6 +191,12 @@ final class DisplayPowerController { // The dim screen brightness. private final int mScreenBrightnessDimConfig; + // The minimum allowed brightness. + private final int mScreenBrightnessRangeMinimum; + + // The maximum allowed brightness. + private final int mScreenBrightnessRangeMaximum; + // True if auto-brightness should be used. private boolean mUseSoftwareAutoBrightnessConfig; @@ -196,6 +209,10 @@ final class DisplayPowerController { // May be 0 if no warm-up is required. private int mLightSensorWarmUpTimeConfig; + // True if we should fade the screen while turning it off, false if we should play + // a stylish electron beam animation instead. + private boolean mElectronBeamFadesConfig; + // The pending power request. // Initially null until the first call to requestPowerState. // Guarded by mLock. @@ -255,6 +272,12 @@ final class DisplayPowerController { // When the screen turns on again, we report user activity to the power manager. private boolean mScreenOffBecauseOfProximity; + // True if the screen on is being blocked. + private boolean mScreenOnWasBlocked; + + // The elapsed real time when the screen on was blocked. + private long mScreenOnBlockStartRealTime; + // Set to true if the light sensor is enabled. private boolean mLightSensorEnabled; @@ -323,10 +346,12 @@ final class DisplayPowerController { */ public DisplayPowerController(Looper looper, Context context, Notifier notifier, LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker, + DisplayBlanker displayBlanker, Callbacks callbacks, Handler callbackHandler) { mHandler = new DisplayControllerHandler(looper); mNotifier = notifier; mSuspendBlocker = suspendBlocker; + mDisplayBlanker = displayBlanker; mCallbacks = callbacks; mCallbackHandler = callbackHandler; @@ -336,8 +361,14 @@ final class DisplayPowerController { mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); final Resources resources = context.getResources(); - mScreenBrightnessDimConfig = resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessDim); + + mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessDim)); + + int screenBrightnessMinimum = Math.min(resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessSettingMinimum), + mScreenBrightnessDimConfig); + mUseSoftwareAutoBrightnessConfig = resources.getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); if (mUseSoftwareAutoBrightnessConfig) { @@ -355,12 +386,22 @@ final class DisplayPowerController { + "which must be strictly increasing. " + "Auto-brightness will be disabled."); mUseSoftwareAutoBrightnessConfig = false; + } else { + if (screenBrightness[0] < screenBrightnessMinimum) { + screenBrightnessMinimum = screenBrightness[0]; + } } mLightSensorWarmUpTimeConfig = resources.getInteger( com.android.internal.R.integer.config_lightSensorWarmupTime); } + mScreenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightnessMinimum); + mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON; + + mElectronBeamFadesConfig = resources.getBoolean( + com.android.internal.R.bool.config_animateScreenLights); + if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) { mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); if (mProximitySensor != null) { @@ -384,14 +425,14 @@ final class DisplayPowerController { final int n = brightness.length; float[] x = new float[n]; float[] y = new float[n]; - y[0] = (float)brightness[0] / PowerManager.BRIGHTNESS_ON; + y[0] = normalizeAbsoluteBrightness(brightness[0]); for (int i = 1; i < n; i++) { x[i] = lux[i - 1]; - y[i] = (float)brightness[i] / PowerManager.BRIGHTNESS_ON; + y[i] = normalizeAbsoluteBrightness(brightness[i]); } Spline spline = Spline.createMonotoneCubicSpline(x, y); - if (false) { + if (DEBUG) { Slog.d(TAG, "Auto-brightness spline: " + spline); for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) { Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v))); @@ -480,10 +521,12 @@ final class DisplayPowerController { private void initialize() { final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); - mPowerState = new DisplayPowerState(new ElectronBeam(display), + mPowerState = new DisplayPowerState( + new ElectronBeam(display), new PhotonicModulator(executor, mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT), - mSuspendBlocker)); + mSuspendBlocker), + mDisplayBlanker); mElectronBeamOnAnimator = ObjectAnimator.ofFloat( mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f); @@ -562,6 +605,7 @@ final class DisplayPowerController { if (!mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity = true; + sendOnProximityPositive(); setScreenOn(false); } } else if (mWaitingForNegativeProximity @@ -576,7 +620,6 @@ final class DisplayPowerController { if (mScreenOffBecauseOfProximity && mProximity != PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity = false; - setScreenOn(true); sendOnProximityNegative(); } } else { @@ -590,30 +633,31 @@ final class DisplayPowerController { } // Set the screen brightness. - if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) { - // Screen is dimmed. Overrides everything else. - animateScreenBrightness( - clampScreenBrightness(mScreenBrightnessDimConfig), - BRIGHTNESS_RAMP_RATE_FAST); - mUsingScreenAutoBrightness = false; - } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) { + if (wantScreenOn(mPowerRequest.screenState)) { + int target; + boolean slow; if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) { // Use current auto-brightness value. - animateScreenBrightness( - clampScreenBrightness(mScreenAutoBrightness), - mUsingScreenAutoBrightness ? BRIGHTNESS_RAMP_RATE_SLOW : - BRIGHTNESS_RAMP_RATE_FAST); + target = mScreenAutoBrightness; + slow = mUsingScreenAutoBrightness; mUsingScreenAutoBrightness = true; } else { // Light sensor is disabled or not ready yet. // Use the current brightness setting from the request, which is expected // provide a nominal default value for the case where auto-brightness // is not ready yet. - animateScreenBrightness( - clampScreenBrightness(mPowerRequest.screenBrightness), - BRIGHTNESS_RAMP_RATE_FAST); + target = mPowerRequest.screenBrightness; + slow = false; mUsingScreenAutoBrightness = false; } + if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) { + // Screen is dimmed. Sets an upper bound on everything else. + target = Math.min(target - SCREEN_DIM_MINIMUM_REDUCTION, + mScreenBrightnessDimConfig); + slow = false; + } + animateScreenBrightness(clampScreenBrightness(target), + slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST); } else { // Screen is off. Don't bother changing the brightness. mUsingScreenAutoBrightness = false; @@ -627,20 +671,33 @@ final class DisplayPowerController { // It is relatively short but if we cancel it and switch to the // on animation immediately then the results are pretty ugly. if (!mElectronBeamOffAnimator.isStarted()) { + // Turn the screen on. The contents of the screen may not yet + // be visible if the electron beam has not been dismissed because + // its last frame of animation is solid black. setScreenOn(true); - if (USE_ELECTRON_BEAM_ON_ANIMATION) { - if (!mElectronBeamOnAnimator.isStarted()) { - if (mPowerState.getElectronBeamLevel() == 1.0f) { - mPowerState.dismissElectronBeam(); - } else if (mPowerState.prepareElectronBeam(true)) { - mElectronBeamOnAnimator.start(); - } else { - mElectronBeamOnAnimator.end(); + + if (mPowerRequest.blockScreenOn + && mPowerState.getElectronBeamLevel() == 0.0f) { + blockScreenOn(); + } else { + unblockScreenOn(); + if (USE_ELECTRON_BEAM_ON_ANIMATION) { + if (!mElectronBeamOnAnimator.isStarted()) { + if (mPowerState.getElectronBeamLevel() == 1.0f) { + mPowerState.dismissElectronBeam(); + } else if (mPowerState.prepareElectronBeam( + mElectronBeamFadesConfig ? + ElectronBeam.MODE_FADE : + ElectronBeam.MODE_WARM_UP)) { + mElectronBeamOnAnimator.start(); + } else { + mElectronBeamOnAnimator.end(); + } } + } else { + mPowerState.setElectronBeamLevel(1.0f); + mPowerState.dismissElectronBeam(); } - } else { - mPowerState.setElectronBeamLevel(1.0f); - mPowerState.dismissElectronBeam(); } } } else { @@ -650,7 +707,10 @@ final class DisplayPowerController { if (!mElectronBeamOffAnimator.isStarted()) { if (mPowerState.getElectronBeamLevel() == 0.0f) { setScreenOn(false); - } else if (mPowerState.prepareElectronBeam(false) + } else if (mPowerState.prepareElectronBeam( + mElectronBeamFadesConfig ? + ElectronBeam.MODE_FADE : + ElectronBeam.MODE_COOL_DOWN) && mPowerState.isScreenOn()) { mElectronBeamOffAnimator.start(); } else { @@ -665,18 +725,43 @@ final class DisplayPowerController { // We mostly care about the screen state here, ignoring brightness changes // which will be handled asynchronously. if (mustNotify + && !mScreenOnWasBlocked && !mElectronBeamOnAnimator.isStarted() && !mElectronBeamOffAnimator.isStarted() && mPowerState.waitUntilClean(mCleanListener)) { synchronized (mLock) { if (!mPendingRequestChangedLocked) { mDisplayReadyLocked = true; + + if (DEBUG) { + Slog.d(TAG, "Display ready!"); + } } } sendOnStateChanged(); } } + private void blockScreenOn() { + if (!mScreenOnWasBlocked) { + mScreenOnWasBlocked = true; + if (DEBUG) { + Slog.d(TAG, "Blocked screen on."); + mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime(); + } + } + } + + private void unblockScreenOn() { + if (mScreenOnWasBlocked) { + mScreenOnWasBlocked = false; + if (DEBUG) { + Slog.d(TAG, "Unblocked screen on after " + + (SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime) + " ms"); + } + } + } + private void setScreenOn(boolean on) { if (!mPowerState.isScreenOn() == on) { mPowerState.setScreenOn(on); @@ -689,7 +774,25 @@ final class DisplayPowerController { } private int clampScreenBrightness(int value) { - return Math.min(Math.max(Math.max(value, mScreenBrightnessDimConfig), 0), 255); + return clamp(value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); + } + + private static int clampAbsoluteBrightness(int value) { + return clamp(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); + } + + private static int clamp(int value, int min, int max) { + if (value <= min) { + return min; + } + if (value >= max) { + return max; + } + return value; + } + + private static float normalizeAbsoluteBrightness(int value) { + return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON; } private void animateScreenBrightness(int target, int rate) { @@ -734,8 +837,13 @@ final class DisplayPowerController { // Only accept a proximity sensor reading if it remains // stable for the entire debounce delay. mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); - mPendingProximity = positive ? PROXIMITY_POSITIVE : PROXIMITY_NEGATIVE; - mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_DEBOUNCE_DELAY; + if (positive) { + mPendingProximity = PROXIMITY_POSITIVE; + mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY; + } else { + mPendingProximity = PROXIMITY_NEGATIVE; + mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY; + } debounceProximitySensor(); } @@ -973,6 +1081,17 @@ final class DisplayPowerController { } }; + private void sendOnProximityPositive() { + mCallbackHandler.post(mOnProximityPositiveRunnable); + } + + private final Runnable mOnProximityPositiveRunnable = new Runnable() { + @Override + public void run() { + mCallbacks.onProximityPositive(); + } + }; + private void sendOnProximityNegative() { mCallbackHandler.post(mOnProximityNegativeRunnable); } @@ -999,6 +1118,8 @@ final class DisplayPowerController { pw.println(); pw.println("Display Controller Configuration:"); pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); + pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); + pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig); pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline); @@ -1090,6 +1211,7 @@ final class DisplayPowerController { */ public interface Callbacks { void onStateChanged(); + void onProximityPositive(); void onProximityNegative(); } diff --git a/services/java/com/android/server/power/DisplayPowerRequest.java b/services/java/com/android/server/power/DisplayPowerRequest.java index 2d74292..22f17d7 100644 --- a/services/java/com/android/server/power/DisplayPowerRequest.java +++ b/services/java/com/android/server/power/DisplayPowerRequest.java @@ -52,12 +52,23 @@ final class DisplayPowerRequest { // If true, enables automatic brightness control. public boolean useAutoBrightness; + // If true, prevents the screen from completely turning on if it is currently off. + // The display does not enter a "ready" state if this flag is true and screen on is + // blocked. The window manager policy blocks screen on while it prepares the keyguard to + // prevent the user from seeing intermediate updates. + // + // Technically, we may not block the screen itself from turning on (because that introduces + // extra unnecessary latency) but we do prevent content on screen from becoming + // visible to the user. + public boolean blockScreenOn; + public DisplayPowerRequest() { screenState = SCREEN_STATE_BRIGHT; useProximitySensor = false; screenBrightness = PowerManager.BRIGHTNESS_ON; screenAutoBrightnessAdjustment = 0.0f; useAutoBrightness = false; + blockScreenOn = false; } public DisplayPowerRequest(DisplayPowerRequest other) { @@ -70,6 +81,7 @@ final class DisplayPowerRequest { screenBrightness = other.screenBrightness; screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment; useAutoBrightness = other.useAutoBrightness; + blockScreenOn = other.blockScreenOn; } @Override @@ -84,7 +96,8 @@ final class DisplayPowerRequest { && useProximitySensor == other.useProximitySensor && screenBrightness == other.screenBrightness && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment - && useAutoBrightness == other.useAutoBrightness; + && useAutoBrightness == other.useAutoBrightness + && blockScreenOn == other.blockScreenOn; } @Override @@ -98,6 +111,7 @@ final class DisplayPowerRequest { + ", useProximitySensor=" + useProximitySensor + ", screenBrightness=" + screenBrightness + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment - + ", useAutoBrightness=" + useAutoBrightness; + + ", useAutoBrightness=" + useAutoBrightness + + ", blockScreenOn=" + blockScreenOn; } } diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java index 1bd7811..fdfcacc 100644 --- a/services/java/com/android/server/power/DisplayPowerState.java +++ b/services/java/com/android/server/power/DisplayPowerState.java @@ -51,7 +51,8 @@ final class DisplayPowerState { private final Choreographer mChoreographer; private final ElectronBeam mElectronBeam; - private final PhotonicModulator mScreenBrightnessModulator; + private final PhotonicModulator mPhotonicModulator; + private final DisplayBlanker mDisplayBlanker; private int mDirty; private boolean mScreenOn; @@ -61,10 +62,11 @@ final class DisplayPowerState { private Runnable mCleanListener; public DisplayPowerState(ElectronBeam electronBean, - PhotonicModulator screenBrightnessModulator) { + PhotonicModulator photonicModulator, DisplayBlanker displayBlanker) { mChoreographer = Choreographer.getInstance(); mElectronBeam = electronBean; - mScreenBrightnessModulator = screenBrightnessModulator; + mPhotonicModulator = photonicModulator; + mDisplayBlanker = displayBlanker; // At boot time, we know that the screen is on and the electron beam // animation is not playing. We don't know the screen's brightness though, @@ -130,13 +132,12 @@ final class DisplayPowerState { * This method should be called before starting an animation because it * can take a fair amount of time to prepare the electron beam surface. * - * @param warmUp True if the electron beam should start warming up. + * @param mode The electron beam animation mode to prepare. * @return True if the electron beam was prepared. */ - public boolean prepareElectronBeam(boolean warmUp) { - boolean success = mElectronBeam.prepare(warmUp); + public boolean prepareElectronBeam(int mode) { invalidate(DIRTY_ELECTRON_BEAM); - return success; + return mElectronBeam.prepare(mode); } /** @@ -239,8 +240,8 @@ final class DisplayPowerState { private void apply() { if (mDirty != 0) { if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) { - mScreenBrightnessModulator.setBrightness(0, true /*sync*/); - PowerManagerService.nativeSetScreenState(false); + mPhotonicModulator.setBrightness(0, true /*sync*/); + mDisplayBlanker.blankAllDisplays(); } if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) { @@ -248,12 +249,12 @@ final class DisplayPowerState { } if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) { - PowerManagerService.nativeSetScreenState(true); + mDisplayBlanker.unblankAllDisplays(); } if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0 && mScreenOn) { - mScreenBrightnessModulator.setBrightness( + mPhotonicModulator.setBrightness( (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/); } diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java index 0c68997..6a567ba 100644 --- a/services/java/com/android/server/power/ElectronBeam.java +++ b/services/java/com/android/server/power/ElectronBeam.java @@ -26,7 +26,6 @@ import android.opengl.EGLSurface; import android.opengl.GLES10; import android.opengl.GLUtils; import android.os.Looper; -import android.os.Process; import android.util.FloatMath; import android.util.Slog; import android.view.Display; @@ -41,12 +40,13 @@ import java.nio.FloatBuffer; /** * Bzzzoooop! *crackle* - * + * <p> * Animates a screen transition from on to off or off to on by applying * some GL transformations to a screenshot. - * + * </p><p> * This component must only be created or accessed by the {@link Looper} thread * that belongs to the {@link DisplayPowerController}. + * </p> */ final class ElectronBeam { private static final String TAG = "ElectronBeam"; @@ -65,7 +65,7 @@ final class ElectronBeam { // Set to true when the animation context has been fully prepared. private boolean mPrepared; - private boolean mWarmUp; + private int mMode; private final Display mDisplay; private final DisplayInfo mDisplayInfo = new DisplayInfo(); @@ -80,6 +80,7 @@ final class ElectronBeam { private EGLContext mEglContext; private EGLSurface mEglSurface; private boolean mSurfaceVisible; + private float mSurfaceAlpha; // Texture names. We only use one texture, which contains the screenshot. private final int[] mTexNames = new int[1]; @@ -90,6 +91,21 @@ final class ElectronBeam { private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8); private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8); + /** + * Animates an electron beam warming up. + */ + public static final int MODE_WARM_UP = 0; + + /** + * Animates an electron beam shutting off. + */ + public static final int MODE_COOL_DOWN = 1; + + /** + * Animates a simple dim layer to fade the contents of the screen in or out progressively. + */ + public static final int MODE_FADE = 2; + public ElectronBeam(Display display) { mDisplay = display; } @@ -98,16 +114,15 @@ final class ElectronBeam { * Warms up the electron beam in preparation for turning on or off. * This method prepares a GL context, and captures a screen shot. * - * @param warmUp True if the electron beam is about to be turned on, false if - * it is about to be turned off. + * @param mode The desired mode for the upcoming animation. * @return True if the electron beam is ready, false if it is uncontrollable. */ - public boolean prepare(boolean warmUp) { + public boolean prepare(int mode) { if (DEBUG) { - Slog.d(TAG, "prepare: warmUp=" + warmUp); + Slog.d(TAG, "prepare: mode=" + mode); } - mWarmUp = warmUp; + mMode = mode; // Get the display size and adjust it for rotation. mDisplay.getDisplayInfo(mDisplayInfo); @@ -123,17 +138,28 @@ final class ElectronBeam { } // Prepare the surface for drawing. - if (!createEglContext() - || !createEglSurface() - || !captureScreenshotTextureAndSetViewport()) { + if (!tryPrepare()) { dismiss(); return false; } + // Done. mPrepared = true; return true; } + private boolean tryPrepare() { + if (createSurface()) { + if (mMode == MODE_FADE) { + return true; + } + return createEglContext() + && createEglSurface() + && captureScreenshotTextureAndSetViewport(); + } + return false; + } + /** * Dismisses the electron beam animation surface and cleans up. * @@ -148,6 +174,7 @@ final class ElectronBeam { destroyScreenshotTexture(); destroyEglSurface(); + destroySurface(); mPrepared = false; } @@ -163,6 +190,14 @@ final class ElectronBeam { Slog.d(TAG, "drawFrame: level=" + level); } + if (!mPrepared) { + return false; + } + + if (mMode == MODE_FADE) { + return showSurface(1.0f - level); + } + if (!attachEglContext()) { return false; } @@ -185,8 +220,7 @@ final class ElectronBeam { } finally { detachEglContext(); } - - return showEglSurface(); + return showSurface(1.0f); } /** @@ -217,7 +251,7 @@ final class ElectronBeam { // bind texture and set blending for drawing planes GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]); GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE, - mWarmUp ? GLES10.GL_MODULATE : GLES10.GL_REPLACE); + mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE); GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR); GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, @@ -251,7 +285,7 @@ final class ElectronBeam { GLES10.glColorMask(true, true, true, true); // draw the white highlight (we use the last vertices) - if (!mWarmUp) { + if (mMode == MODE_COOL_DOWN) { GLES10.glColor4f(ag, ag, ag, 1.0f); GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); } @@ -472,7 +506,7 @@ final class ElectronBeam { } }*/ - private boolean createEglSurface() { + private boolean createSurface() { if (mSurfaceSession == null) { mSurfaceSession = new SurfaceSession(); } @@ -481,9 +515,15 @@ final class ElectronBeam { try { if (mSurface == null) { try { + int flags; + if (mMode == MODE_FADE) { + flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN; + } else { + flags = Surface.OPAQUE | Surface.HIDDEN; + } mSurface = new Surface(mSurfaceSession, "ElectronBeam", mDisplayWidth, mDisplayHeight, - PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN); + PixelFormat.OPAQUE, flags); } catch (Surface.OutOfResourcesException ex) { Slog.e(TAG, "Unable to create surface.", ex); return false; @@ -514,7 +554,10 @@ final class ElectronBeam { } finally { Surface.closeTransaction(); } + return true; + } + private boolean createEglSurface() { if (mEglSurface == null) { int[] eglSurfaceAttribList = new int[] { EGL14.EGL_NONE @@ -536,7 +579,9 @@ final class ElectronBeam { } mEglSurface = null; } + } + private void destroySurface() { if (mSurface != null) { Surface.openTransaction(); try { @@ -546,19 +591,22 @@ final class ElectronBeam { } mSurface = null; mSurfaceVisible = false; + mSurfaceAlpha = 0f; } } - private boolean showEglSurface() { - if (!mSurfaceVisible) { + private boolean showSurface(float alpha) { + if (!mSurfaceVisible || mSurfaceAlpha != alpha) { Surface.openTransaction(); try { mSurface.setLayer(ELECTRON_BEAM_LAYER); + mSurface.setAlpha(alpha); mSurface.show(); } finally { Surface.closeTransaction(); } mSurfaceVisible = true; + mSurfaceAlpha = alpha; } return true; } @@ -643,11 +691,12 @@ final class ElectronBeam { pw.println(); pw.println("Electron Beam State:"); pw.println(" mPrepared=" + mPrepared); - pw.println(" mWarmUp=" + mWarmUp); + pw.println(" mMode=" + mMode); pw.println(" mDisplayLayerStack=" + mDisplayLayerStack); pw.println(" mDisplayRotation=" + mDisplayRotation); pw.println(" mDisplayWidth=" + mDisplayWidth); pw.println(" mDisplayHeight=" + mDisplayHeight); pw.println(" mSurfaceVisible=" + mSurfaceVisible); + pw.println(" mSurfaceAlpha=" + mSurfaceAlpha); } } diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java index ce1e147..5e05693 100644 --- a/services/java/com/android/server/power/Notifier.java +++ b/services/java/com/android/server/power/Notifier.java @@ -35,21 +35,23 @@ import android.os.WorkSource; import android.util.EventLog; import android.util.Slog; import android.view.WindowManagerPolicy; -import android.view.WindowManagerPolicy.ScreenOnListener; /** * Sends broadcasts about important power state changes. - * + * <p> * This methods of this class may be called by the power manager service while * its lock is being held. Internally it takes care of sending broadcasts to * notify other components of the system or applications asynchronously. - * + * </p><p> * The notifier is designed to collapse unnecessary broadcasts when it is not * possible for the system to have observed an intermediate state. - * - * For example, if the device wakes up, goes to sleep and wakes up again immediately - * before the go to sleep broadcast has been sent, then no broadcast will be - * sent about the system going to sleep and waking up. + * </p><p> + * For example, if the device wakes up, goes to sleep, wakes up again and goes to + * sleep again before the wake up notification is sent, then the system will + * be told about only one wake up and sleep. However, we always notify the + * fact that at least one transition occurred. It is especially important to + * tell the system when we go to sleep so that it can lock the keyguard if needed. + * </p> */ final class Notifier { private static final String TAG = "PowerManagerNotifier"; @@ -68,8 +70,8 @@ final class Notifier { private final Context mContext; private final IBatteryStats mBatteryStats; private final SuspendBlocker mSuspendBlocker; + private final ScreenOnBlocker mScreenOnBlocker; private final WindowManagerPolicy mPolicy; - private final ScreenOnListener mScreenOnListener; private final NotifierHandler mHandler; private final Intent mScreenOnIntent; @@ -79,6 +81,10 @@ final class Notifier { private int mActualPowerState; private int mLastGoToSleepReason; + // True if there is a pending transition that needs to be reported. + private boolean mPendingWakeUpBroadcast; + private boolean mPendingGoToSleepBroadcast; + // The currently broadcasted power state. This reflects what other parts of the // system have observed. private int mBroadcastedPowerState; @@ -88,14 +94,17 @@ final class Notifier { // True if a user activity message should be sent. private boolean mUserActivityPending; + // True if the screen on blocker has been acquired. + private boolean mScreenOnBlockerAcquired; + public Notifier(Looper looper, Context context, IBatteryStats batteryStats, - SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - ScreenOnListener screenOnListener) { + SuspendBlocker suspendBlocker, ScreenOnBlocker screenOnBlocker, + WindowManagerPolicy policy) { mContext = context; mBatteryStats = batteryStats; mSuspendBlocker = suspendBlocker; + mScreenOnBlocker = screenOnBlocker; mPolicy = policy; - mScreenOnListener = screenOnListener; mHandler = new NotifierHandler(looper); mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); @@ -219,6 +228,11 @@ final class Notifier { synchronized (mLock) { if (mActualPowerState != POWER_STATE_AWAKE) { mActualPowerState = POWER_STATE_AWAKE; + mPendingWakeUpBroadcast = true; + if (!mScreenOnBlockerAcquired) { + mScreenOnBlockerAcquired = true; + mScreenOnBlocker.acquire(); + } updatePendingBroadcastLocked(); } } @@ -264,6 +278,7 @@ final class Notifier { synchronized (mLock) { if (mActualPowerState != POWER_STATE_ASLEEP) { mActualPowerState = POWER_STATE_ASLEEP; + mPendingGoToSleepBroadcast = true; if (mUserActivityPending) { mUserActivityPending = false; mHandler.removeMessages(MSG_USER_ACTIVITY); @@ -300,7 +315,8 @@ final class Notifier { private void updatePendingBroadcastLocked() { if (!mBroadcastInProgress && mActualPowerState != POWER_STATE_UNKNOWN - && mActualPowerState != mBroadcastedPowerState) { + && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast + || mActualPowerState != mBroadcastedPowerState)) { mBroadcastInProgress = true; mSuspendBlocker.acquire(); Message msg = mHandler.obtainMessage(MSG_BROADCAST); @@ -309,6 +325,11 @@ final class Notifier { } } + private void finishPendingBroadcastLocked() { + mBroadcastInProgress = false; + mSuspendBlocker.release(); + } + private void sendUserActivity() { synchronized (mLock) { if (!mUserActivityPending) { @@ -324,18 +345,35 @@ final class Notifier { final int powerState; final int goToSleepReason; synchronized (mLock) { - if (mActualPowerState == POWER_STATE_UNKNOWN - || mActualPowerState == mBroadcastedPowerState) { - mBroadcastInProgress = false; - mSuspendBlocker.release(); - return; + if (mBroadcastedPowerState == POWER_STATE_UNKNOWN) { + // Broadcasted power state is unknown. Send wake up. + mPendingWakeUpBroadcast = false; + mBroadcastedPowerState = POWER_STATE_AWAKE; + } else if (mBroadcastedPowerState == POWER_STATE_AWAKE) { + // Broadcasted power state is awake. Send asleep if needed. + if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast + || mActualPowerState == POWER_STATE_ASLEEP) { + mPendingGoToSleepBroadcast = false; + mBroadcastedPowerState = POWER_STATE_ASLEEP; + } else { + finishPendingBroadcastLocked(); + return; + } + } else { + // Broadcasted power state is asleep. Send awake if needed. + if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast + || mActualPowerState == POWER_STATE_AWAKE) { + mPendingWakeUpBroadcast = false; + mBroadcastedPowerState = POWER_STATE_AWAKE; + } else { + finishPendingBroadcastLocked(); + return; + } } - powerState = mActualPowerState; - goToSleepReason = mLastGoToSleepReason; - - mBroadcastedPowerState = powerState; mBroadcastStartTime = SystemClock.uptimeMillis(); + powerState = mBroadcastedPowerState; + goToSleepReason = mLastGoToSleepReason; } EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1); @@ -355,6 +393,7 @@ final class Notifier { EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0); mPolicy.screenTurningOn(mScreenOnListener); + try { ActivityManagerNative.getDefault().wakingUp(); } catch (RemoteException e) { @@ -370,6 +409,19 @@ final class Notifier { } } + private final WindowManagerPolicy.ScreenOnListener mScreenOnListener = + new WindowManagerPolicy.ScreenOnListener() { + @Override + public void onScreenOn() { + synchronized (mLock) { + if (mScreenOnBlockerAcquired && !mPendingWakeUpBroadcast) { + mScreenOnBlockerAcquired = false; + mScreenOnBlocker.release(); + } + } + } + }; + private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 9a01022..b9085a4 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -79,6 +79,8 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int MSG_USER_ACTIVITY_TIMEOUT = 1; // Message: Sent when the device enters or exits a napping or dreaming state. private static final int MSG_SANDMAN = 2; + // Message: Sent when the screen on blocker is released. + private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3; // Dirty bit: mWakeLocks changed private static final int DIRTY_WAKE_LOCKS = 1 << 0; @@ -98,6 +100,10 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int DIRTY_STAY_ON = 1 << 7; // Dirty bit: battery state changed private static final int DIRTY_BATTERY_STATE = 1 << 8; + // Dirty bit: proximity state changed + private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9; + // Dirty bit: screen on blocker state became held or unheld + private static final int DIRTY_SCREEN_ON_BLOCKER_RELEASED = 1 << 10; // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp(). // The screen should be off or in the process of being turned off by the display controller. @@ -121,6 +127,7 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2; private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3; private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4; + private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // only set if already awake // Summarizes the user activity state. private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0; @@ -149,6 +156,7 @@ public final class PowerManagerService extends IPowerManager.Stub private Context mContext; private LightsService mLightsService; private BatteryService mBatteryService; + private DisplayManagerService mDisplayManagerService; private IBatteryStats mBatteryStats; private HandlerThread mHandlerThread; private PowerManagerHandler mHandler; @@ -220,6 +228,13 @@ public final class PowerManagerService extends IPowerManager.Stub // The suspend blocker used to keep the CPU alive when wake locks have been acquired. private final SuspendBlocker mWakeLockSuspendBlocker; + // The screen on blocker used to keep the screen from turning on while the lock + // screen is coming up. + private final ScreenOnBlockerImpl mScreenOnBlocker; + + // The display blanker used to turn the screen on or off. + private final DisplayBlankerImpl mDisplayBlanker; + // True if systemReady() has been called. private boolean mSystemReady; @@ -258,6 +273,9 @@ public final class PowerManagerService extends IPowerManager.Stub // True if the device should stay on. private boolean mStayOn; + // True if the proximity sensor reads a positive result. + private boolean mProximityPositive; + // Screen brightness setting limits. private int mScreenBrightnessSettingMinimum; private int mScreenBrightnessSettingMaximum; @@ -306,13 +324,15 @@ public final class PowerManagerService extends IPowerManager.Stub private static native void nativeSetPowerState(boolean screenOn, boolean screenBright); private static native void nativeAcquireSuspendBlocker(String name); private static native void nativeReleaseSuspendBlocker(String name); - - static native void nativeSetScreenState(boolean on); + private static native void nativeSetInteractive(boolean enable); + private static native void nativeSetAutoSuspend(boolean enable); public PowerManagerService() { synchronized (mLock) { mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService"); mWakeLockSuspendBlocker.acquire(); + mScreenOnBlocker = new ScreenOnBlockerImpl(); + mDisplayBlanker = new DisplayBlankerImpl(); mHoldingWakeLockSuspendBlocker = true; mWakefulness = WAKEFULNESS_AWAKE; } @@ -328,23 +348,24 @@ public final class PowerManagerService extends IPowerManager.Stub public void init(Context context, LightsService ls, ActivityManagerService am, BatteryService bs, IBatteryStats bss, DisplayManagerService dm) { - // Forcibly turn the screen on at boot so that it is in a known power state. - // We do this in init() rather than in the constructor because setting the - // screen state requires a call into surface flinger which then needs to call back - // into the activity manager to check permissions. Unfortunately the - // activity manager is not running when the constructor is called, so we - // have to defer setting the screen state until this point. - nativeSetScreenState(true); - mContext = context; mLightsService = ls; mBatteryService = bs; mBatteryStats = bss; + mDisplayManagerService = dm; mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); mHandler = new PowerManagerHandler(mHandlerThread.getLooper()); Watchdog.getInstance().addMonitor(this); + + // Forcibly turn the screen on at boot so that it is in a known power state. + // We do this in init() rather than in the constructor because setting the + // screen state requires a call into surface flinger which then needs to call back + // into the activity manager to check permissions. Unfortunately the + // activity manager is not running when the constructor is called, so we + // have to defer setting the screen state until this point. + mDisplayBlanker.unblankAllDisplays(); } public void setPolicy(WindowManagerPolicy policy) { @@ -363,13 +384,18 @@ public final class PowerManagerService extends IPowerManager.Stub mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting(); - mNotifier = new Notifier(mHandler.getLooper(), mContext, mBatteryStats, + // The notifier runs on the system server's main looper so as not to interfere + // with the animations and other critical functions of the power manager. + mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats, createSuspendBlockerLocked("PowerManagerService.Broadcasts"), - mPolicy, mScreenOnListener); + mScreenOnBlocker, mPolicy); + + // The display power controller runs on the power manager service's + // own handler thread. mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(), mContext, mNotifier, mLightsService, twilight, createSuspendBlockerLocked("PowerManagerService.Display"), - mDisplayPowerControllerCallbacks, mHandler); + mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler); mSettingsObserver = new SettingsObserver(mHandler); mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION); @@ -823,9 +849,9 @@ public final class PowerManagerService extends IPowerManager.Stub switch (mWakefulness) { case WAKEFULNESS_ASLEEP: Slog.i(TAG, "Waking up from sleep..."); + sendPendingNotificationsLocked(); mNotifier.onWakeUpStarted(); mSendWakeUpFinishedNotificationWhenReady = true; - mSendGoToSleepFinishedNotificationWhenReady = false; break; case WAKEFULNESS_DREAMING: Slog.i(TAG, "Waking up from dream..."); @@ -896,12 +922,13 @@ public final class PowerManagerService extends IPowerManager.Stub break; } + sendPendingNotificationsLocked(); + mNotifier.onGoToSleepStarted(reason); + mSendGoToSleepFinishedNotificationWhenReady = true; + mLastSleepTime = eventTime; mDirty |= DIRTY_WAKEFULNESS; mWakefulness = WAKEFULNESS_ASLEEP; - mNotifier.onGoToSleepStarted(reason); - mSendGoToSleepFinishedNotificationWhenReady = true; - mSendWakeUpFinishedNotificationWhenReady = false; // Report the number of wake locks that will be cleared by going to sleep. int numWakeLocksCleared = 0; @@ -1000,7 +1027,9 @@ public final class PowerManagerService extends IPowerManager.Stub updateDisplayPowerStateLocked(dirtyPhase2); // Phase 3: Send notifications, if needed. - sendPendingNotificationsLocked(); + if (mDisplayReady) { + sendPendingNotificationsLocked(); + } // Phase 4: Update suspend blocker. // Because we might release the last suspend blocker here, we need to make sure @@ -1009,15 +1038,13 @@ public final class PowerManagerService extends IPowerManager.Stub } private void sendPendingNotificationsLocked() { - if (mDisplayReady) { - if (mSendWakeUpFinishedNotificationWhenReady) { - mSendWakeUpFinishedNotificationWhenReady = false; - mNotifier.onWakeUpFinished(); - } - if (mSendGoToSleepFinishedNotificationWhenReady) { - mSendGoToSleepFinishedNotificationWhenReady = false; - mNotifier.onGoToSleepFinished(); - } + if (mSendWakeUpFinishedNotificationWhenReady) { + mSendWakeUpFinishedNotificationWhenReady = false; + mNotifier.onWakeUpFinished(); + } + if (mSendGoToSleepFinishedNotificationWhenReady) { + mSendGoToSleepFinishedNotificationWhenReady = false; + mNotifier.onGoToSleepFinished(); } } @@ -1058,41 +1085,51 @@ public final class PowerManagerService extends IPowerManager.Stub } private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(boolean wasPowered, int oldPlugType) { - if (mWakeUpWhenPluggedOrUnpluggedConfig) { - // FIXME: Need more accurate detection of wireless chargers. - // - // We are unable to accurately detect whether the device is resting on the - // charger unless it is actually receiving power. This causes us some grief - // because the device might not appear to be plugged into the wireless charger - // unless it actually charging. - // - // To avoid spuriously waking the screen, we apply a special policy to - // wireless chargers. - // - // 1. Don't wake the device when unplugged from wireless charger because - // it might be that the device is still resting on the wireless charger - // but is not receiving power anymore because the battery is full. - // - // 2. Don't wake the device when plugged into a wireless charger if the - // battery already appears to be mostly full. This situation may indicate - // that the device was resting on the charger the whole time and simply - // wasn't receiving power because the battery was full. We can't tell - // whether the device was just placed on the charger or whether it has - // been there for half of the night slowly discharging until it hit - // the point where it needed to start charging again. - if (wasPowered && !mIsPowered - && oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) { - return false; - } - if (!wasPowered && mIsPowered - && mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS - && mBatteryService.getBatteryLevel() >= - WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) { - return false; - } - return true; + // Don't wake when powered unless configured to do so. + if (!mWakeUpWhenPluggedOrUnpluggedConfig) { + return false; } - return false; + + // FIXME: Need more accurate detection of wireless chargers. + // + // We are unable to accurately detect whether the device is resting on the + // charger unless it is actually receiving power. This causes us some grief + // because the device might not appear to be plugged into the wireless charger + // unless it actually charging. + // + // To avoid spuriously waking the screen, we apply a special policy to + // wireless chargers. + // + // 1. Don't wake the device when unplugged from wireless charger because + // it might be that the device is still resting on the wireless charger + // but is not receiving power anymore because the battery is full. + // + // 2. Don't wake the device when plugged into a wireless charger if the + // battery already appears to be mostly full. This situation may indicate + // that the device was resting on the charger the whole time and simply + // wasn't receiving power because the battery was full. We can't tell + // whether the device was just placed on the charger or whether it has + // been there for half of the night slowly discharging until it hit + // the point where it needed to start charging again. + if (wasPowered && !mIsPowered + && oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) { + return false; + } + if (!wasPowered && mIsPowered + && mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS + && mBatteryService.getBatteryLevel() >= + WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) { + return false; + } + + // If already dreaming and becoming powered, then don't wake. + if (mIsPowered && (mWakefulness == WAKEFULNESS_NAPPING + || mWakefulness == WAKEFULNESS_DREAMING)) { + return false; + } + + // Otherwise wake up! + return true; } /** @@ -1101,12 +1138,17 @@ public final class PowerManagerService extends IPowerManager.Stub */ private void updateStayOnLocked(int dirty) { if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) { + final boolean wasStayOn = mStayOn; if (mStayOnWhilePluggedInSetting != 0 && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting); } else { mStayOn = false; } + + if (mStayOn != wasStayOn) { + mDirty |= DIRTY_STAY_ON; + } } } @@ -1131,16 +1173,25 @@ public final class PowerManagerService extends IPowerManager.Stub if (mWakefulness != WAKEFULNESS_ASLEEP) { mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; + if (mWakefulness == WAKEFULNESS_AWAKE) { + mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; + } } break; case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: if (mWakefulness != WAKEFULNESS_ASLEEP) { mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT; + if (mWakefulness == WAKEFULNESS_AWAKE) { + mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; + } } break; case PowerManager.SCREEN_DIM_WAKE_LOCK: if (mWakefulness != WAKEFULNESS_ASLEEP) { mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM; + if (mWakefulness == WAKEFULNESS_AWAKE) { + mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; + } } break; case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: @@ -1265,7 +1316,7 @@ public final class PowerManagerService extends IPowerManager.Stub private boolean updateWakefulnessLocked(int dirty) { boolean changed = false; if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED - | DIRTY_WAKEFULNESS | DIRTY_STAY_ON)) != 0) { + | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE)) != 0) { if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { if (DEBUG_SPEW) { Slog.d(TAG, "updateWakefulnessLocked: Bed time..."); @@ -1288,17 +1339,17 @@ public final class PowerManagerService extends IPowerManager.Stub * to being fully awake or else go to sleep for good. */ private boolean isItBedTimeYetLocked() { - return mBootCompleted && !isScreenBeingKeptOnLocked(); + return mBootCompleted && !isBeingKeptAwakeLocked(); } /** - * Returns true if the screen is being kept on by a wake lock, user activity + * Returns true if the device is being kept awake by a wake lock, user activity * or the stay on while powered setting. */ - private boolean isScreenBeingKeptOnLocked() { + private boolean isBeingKeptAwakeLocked() { return mStayOn - || (mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM - | WAKE_LOCK_PROXIMITY_SCREEN_OFF)) != 0 + || mProximityPositive + || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0 || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0; } @@ -1314,6 +1365,7 @@ public final class PowerManagerService extends IPowerManager.Stub | DIRTY_SETTINGS | DIRTY_IS_POWERED | DIRTY_STAY_ON + | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) != 0) { scheduleSandmanLocked(); } @@ -1401,7 +1453,7 @@ public final class PowerManagerService extends IPowerManager.Stub && mDreamsEnabledSetting && mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF && mBootCompleted - && (mIsPowered || isScreenBeingKeptOnLocked()); + && (mIsPowered || isBeingKeptAwakeLocked()); } /** @@ -1421,6 +1473,13 @@ public final class PowerManagerService extends IPowerManager.Stub } } + private void handleScreenOnBlockerReleased() { + synchronized (mLock) { + mDirty |= DIRTY_SCREEN_ON_BLOCKER_RELEASED; + updatePowerStateLocked(); + } + } + /** * Updates the display power state asynchronously. * When the update is finished, mDisplayReady will be set to true. The display @@ -1432,8 +1491,8 @@ public final class PowerManagerService extends IPowerManager.Stub private void updateDisplayPowerStateLocked(int dirty) { if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED - | DIRTY_SETTINGS)) != 0) { - int newScreenState = getDesiredScreenPowerState(); + | DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) { + int newScreenState = getDesiredScreenPowerStateLocked(); if (newScreenState != mDisplayPowerRequest.screenState) { if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF && mDisplayPowerRequest.screenState @@ -1481,12 +1540,14 @@ public final class PowerManagerService extends IPowerManager.Stub mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); + mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld(); + mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest, mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; if (DEBUG_SPEW) { - Slog.d(TAG, "updateScreenStateLocked: displayReady=" + mDisplayReady + Slog.d(TAG, "updateScreenStateLocked: mDisplayReady=" + mDisplayReady + ", newScreenState=" + newScreenState + ", mWakefulness=" + mWakefulness + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) @@ -1505,7 +1566,7 @@ public final class PowerManagerService extends IPowerManager.Stub return value >= -1.0f && value <= 1.0f; } - private int getDesiredScreenPowerState() { + private int getDesiredScreenPowerStateLocked() { if (mWakefulness == WAKEFULNESS_ASLEEP) { return DisplayPowerRequest.SCREEN_STATE_OFF; } @@ -1528,7 +1589,16 @@ public final class PowerManagerService extends IPowerManager.Stub } @Override + public void onProximityPositive() { + mProximityPositive = true; + mDirty |= DIRTY_PROXIMITY_POSITIVE; + updatePowerStateLocked(); + } + + @Override public void onProximityNegative() { + mProximityPositive = false; + mDirty |= DIRTY_PROXIMITY_POSITIVE; userActivityNoUpdateLocked(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); updatePowerStateLocked(); @@ -1986,6 +2056,7 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" mIsPowered=" + mIsPowered); pw.println(" mPlugType=" + mPlugType); pw.println(" mStayOn=" + mStayOn); + pw.println(" mProximityPositive=" + mProximityPositive); pw.println(" mBootCompleted=" + mBootCompleted); pw.println(" mSystemReady=" + mSystemReady); pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)); @@ -2048,6 +2119,12 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" " + sb); } + pw.println(); + pw.println("Screen On Blocker: " + mScreenOnBlocker); + + pw.println(); + pw.println("Display Blanker: " + mDisplayBlanker); + dpc = mDisplayPowerController; } @@ -2130,13 +2207,6 @@ public final class PowerManagerService extends IPowerManager.Stub } } - private final WindowManagerPolicy.ScreenOnListener mScreenOnListener = - new WindowManagerPolicy.ScreenOnListener() { - @Override - public void onScreenOn() { - } - }; - /** * Handler for asynchronous operations performed by the power manager. */ @@ -2154,6 +2224,9 @@ public final class PowerManagerService extends IPowerManager.Stub case MSG_SANDMAN: handleSandman(); break; + case MSG_SCREEN_ON_BLOCKER_RELEASED: + handleScreenOnBlockerReleased(); + break; } } } @@ -2299,4 +2372,80 @@ public final class PowerManagerService extends IPowerManager.Stub } } } + + private final class ScreenOnBlockerImpl implements ScreenOnBlocker { + private int mNestCount; + + public boolean isHeld() { + synchronized (this) { + return mNestCount != 0; + } + } + + @Override + public void acquire() { + synchronized (this) { + mNestCount += 1; + if (DEBUG) { + Slog.d(TAG, "Screen on blocked: mNestCount=" + mNestCount); + } + } + } + + @Override + public void release() { + synchronized (this) { + mNestCount -= 1; + if (mNestCount < 0) { + Log.wtf(TAG, "Screen on blocker was released without being acquired!", + new Throwable()); + mNestCount = 0; + } + if (mNestCount == 0) { + mHandler.sendEmptyMessage(MSG_SCREEN_ON_BLOCKER_RELEASED); + } + if (DEBUG) { + Slog.d(TAG, "Screen on unblocked: mNestCount=" + mNestCount); + } + } + } + + @Override + public String toString() { + synchronized (this) { + return "held=" + (mNestCount != 0) + ", mNestCount=" + mNestCount; + } + } + } + + private final class DisplayBlankerImpl implements DisplayBlanker { + private boolean mBlanked; + + @Override + public void blankAllDisplays() { + synchronized (this) { + mBlanked = true; + mDisplayManagerService.blankAllDisplaysFromPowerManager(); + nativeSetInteractive(false); + nativeSetAutoSuspend(true); + } + } + + @Override + public void unblankAllDisplays() { + synchronized (this) { + nativeSetAutoSuspend(false); + nativeSetInteractive(true); + mDisplayManagerService.unblankAllDisplaysFromPowerManager(); + mBlanked = false; + } + } + + @Override + public String toString() { + synchronized (this) { + return "blanked=" + mBlanked; + } + } + } } diff --git a/services/java/com/android/server/power/ScreenOnBlocker.java b/services/java/com/android/server/power/ScreenOnBlocker.java new file mode 100644 index 0000000..dbbbc6d --- /dev/null +++ b/services/java/com/android/server/power/ScreenOnBlocker.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 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.server.power; + +/** + * Low-level screen on blocker mechanism which is used to keep the screen off + * or the contents of the screen hidden until the window manager is ready to show new content. + */ +interface ScreenOnBlocker { + /** + * Acquires the screen on blocker. + * Prevents the screen from turning on. + * + * Calls to acquire() nest and must be matched by the same number + * of calls to release(). + */ + void acquire(); + + /** + * Releases the screen on blocker. + * Allows the screen to turn on. + * + * It is an error to call release() if the screen on blocker has not been acquired. + * The system may crash. + */ + void release(); +} diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java index 13b072c..7efffe5 100644 --- a/services/java/com/android/server/wm/AppWindowToken.java +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -50,6 +50,7 @@ class AppWindowToken extends WindowToken { int groupId = -1; boolean appFullscreen; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + boolean showWhenLocked; // The input dispatching timeout for this application token in nanoseconds. long inputDispatchingTimeoutNanos; diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 61310ca..d966001 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -21,6 +21,7 @@ import com.android.server.input.InputApplicationHandle; import com.android.server.input.InputWindowHandle; import com.android.server.wm.WindowManagerService.AllWindowsIterator; +import android.app.ActivityManagerNative; import android.graphics.Rect; import android.os.RemoteException; import android.util.Log; @@ -89,8 +90,9 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle) { AppWindowToken appWindowToken = null; + WindowState windowState = null; + boolean aboveSystem = false; synchronized (mService.mWindowMap) { - WindowState windowState = null; if (inputWindowHandle != null) { windowState = (WindowState) inputWindowHandle.windowState; if (windowState != null) { @@ -104,6 +106,12 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { if (windowState != null) { Slog.i(WindowManagerService.TAG, "Input event dispatching timed out " + "sending to " + windowState.mAttrs.getTitle()); + // Figure out whether this window is layered above system windows. + // We need to do this here to help the activity manager know how to + // layer its ANR dialog. + int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw( + WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + aboveSystem = windowState.mBaseLayer > systemAlertLayer; } else if (appWindowToken != null) { Slog.i(WindowManagerService.TAG, "Input event dispatching timed out " + "sending to application " + appWindowToken.stringName); @@ -126,6 +134,19 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { } } catch (RemoteException ex) { } + } else if (windowState != null) { + try { + // Notify the activity manager about the timeout and let it decide whether + // to abort dispatching or keep waiting. + long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut( + windowState.mSession.mPid, aboveSystem); + if (timeout >= 0) { + // The activity manager declined to abort dispatching. + // Wait a bit longer and timeout again later. + return timeout; + } + } catch (RemoteException ex) { + } } return 0; // abort dispatching } diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java index d84a52b..3b4c1ab 100644 --- a/services/java/com/android/server/wm/Session.java +++ b/services/java/com/android/server/wm/Session.java @@ -30,8 +30,10 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Slog; import android.view.Display; import android.view.IWindow; @@ -69,8 +71,17 @@ final class Session extends IWindowSession.Stub StringBuilder sb = new StringBuilder(); sb.append("Session{"); sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(" uid "); - sb.append(mUid); + sb.append(" "); + sb.append(mPid); + if (mUid < Process.FIRST_APPLICATION_UID) { + sb.append(":"); + sb.append(mUid); + } else { + sb.append(":u"); + sb.append(UserHandle.getUserId(mUid)); + sb.append('a'); + sb.append(UserHandle.getAppId(mUid)); + } sb.append("}"); mStringName = sb.toString(); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 180579d..037bfde 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD; +import static android.view.WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND; @@ -1209,12 +1210,9 @@ public class WindowManagerService extends IWindowManager.Stub final WindowState curTarget = mInputMethodTarget; if (curTarget != null && w != null && curTarget.isDisplayedLw() - && curTarget.mExiting) { - if (curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) { - w = curTarget; - i = windows.indexOf(w); - if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, switching to: " + w); - } + && (curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) { + if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, not changing"); + return windows.indexOf(curTarget) + 1; } if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Desired input method target=" @@ -2083,6 +2081,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowState attachedWindow = null; WindowState win = null; long origId; + final int type = attrs.type; synchronized(mWindowMap) { if (!mDisplayReady) { @@ -2094,7 +2093,7 @@ public class WindowManagerService extends IWindowManager.Stub return WindowManagerGlobal.ADD_DUPLICATE_ADD; } - if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { + if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { Slog.w(TAG, "Attempted to add window with token that is not a window: " @@ -2112,31 +2111,29 @@ public class WindowManagerService extends IWindowManager.Stub boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { - if (attrs.type >= FIRST_APPLICATION_WINDOW - && attrs.type <= LAST_APPLICATION_WINDOW) { + if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { Slog.w(TAG, "Attempted to add application window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - if (attrs.type == TYPE_INPUT_METHOD) { + if (type == TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - if (attrs.type == TYPE_WALLPAPER) { + if (type == TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - if (attrs.type == TYPE_DREAM) { + if (type == TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } token = new WindowToken(this, attrs.token, -1, false); addToken = true; - } else if (attrs.type >= FIRST_APPLICATION_WINDOW - && attrs.type <= LAST_APPLICATION_WINDOW) { + } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { AppWindowToken atoken = token.appWindowToken; if (atoken == null) { Slog.w(TAG, "Attempted to add window with non-application token " @@ -2147,25 +2144,25 @@ public class WindowManagerService extends IWindowManager.Stub + token + ". Aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } - if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { + if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (localLOGV) Slog.v( TAG, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; } - } else if (attrs.type == TYPE_INPUT_METHOD) { + } else if (type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - } else if (attrs.type == TYPE_WALLPAPER) { + } else if (type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - } else if (attrs.type == TYPE_DREAM) { + } else if (type == TYPE_DREAM) { if (token.windowType != TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with bad token " + attrs.token + ". Aborting."); @@ -2185,6 +2182,7 @@ public class WindowManagerService extends IWindowManager.Stub } mPolicy.adjustWindowParamsLw(win.mAttrs); + win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerGlobal.ADD_OKAY) { @@ -2213,8 +2211,7 @@ public class WindowManagerService extends IWindowManager.Stub win.attach(); mWindowMap.put(client.asBinder(), win); - if (attrs.type == TYPE_APPLICATION_STARTING && - token.appWindowToken != null) { + if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken + " startingWindow=" + win); @@ -2222,19 +2219,19 @@ public class WindowManagerService extends IWindowManager.Stub boolean imMayMove = true; - if (attrs.type == TYPE_INPUT_METHOD) { + if (type == TYPE_INPUT_METHOD) { win.mGivenInsetsPending = true; mInputMethodWindow = win; addInputMethodWindowToListLocked(win); imMayMove = false; - } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) { + } else if (type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); adjustInputMethodDialogsLocked(); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); - if (attrs.type == TYPE_WALLPAPER) { + if (type == TYPE_WALLPAPER) { mLastWallpaperTimeoutTime = 0; adjustWallpaperWindowsLocked(); } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { @@ -2752,10 +2749,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (DEBUG_LAYOUT - // TODO: Remove once b/7094175 is fixed - || ((String)win.mAttrs.getTitle()).contains("Keyguard") - ) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility + if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility + " " + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); win.mEnforceSizeCompat = (win.mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0; @@ -3703,7 +3697,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void addAppToken(int addPos, IApplicationToken token, - int groupId, int requestedOrientation, boolean fullscreen) { + int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3733,6 +3727,7 @@ public class WindowManagerService extends IWindowManager.Stub atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; atoken.groupId = groupId; atoken.appFullscreen = fullscreen; + atoken.showWhenLocked = showWhenLocked; atoken.requestedOrientation = requestedOrientation; if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken + " at " + addPos); @@ -4183,6 +4178,7 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override public void setAppStartingWindow(IBinder token, String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, @@ -5404,6 +5400,22 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { mCurrentUserId = newUserId; mPolicy.setCurrentUserLw(newUserId); + + // Hide windows that should not be seen by the new user. + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + final WindowList windows = iterator.next().getWindowList(); + for (int i = 0; i < windows.size(); i++) { + final WindowState win = windows.get(i); + if (win.isHiddenFromUserLocked()) { + Slog.w(TAG, "current user violation " + newUserId + " hiding " + + win + ", attrs=" + win.mAttrs.type + ", belonging to " + + win.mOwnerUid); + win.hideLw(false); + } + } + } + performLayoutAndPlaceSurfacesLocked(); } } @@ -8221,7 +8233,9 @@ public class WindowManagerService extends IWindowManager.Stub int seq = mLayoutSeq+1; if (seq < 0) seq = 0; mLayoutSeq = seq; - + + boolean behindDream = false; + // First perform layout of any root windows (not attached // to another window). int topAttached = -1; @@ -8231,7 +8245,8 @@ public class WindowManagerService extends IWindowManager.Stub // Don't do layout of a window if it is not visible, or // soon won't be visible, to avoid wasting time and funky // changes while a window is animating away. - final boolean gone = win.isGoneForLayoutLw(); + final boolean gone = (behindDream && mPolicy.canBeForceHidden(win, win.mAttrs)) + || win.isGoneForLayoutLw(); if (DEBUG_LAYOUT && !win.mLayoutAttached) { Slog.v(TAG, "1ST PASS " + win @@ -8266,6 +8281,12 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } + if (win.mAttrs.type == TYPE_DREAM) { + // Don't layout windows behind a dream, so that if it + // does stuff like hide the status bar we won't get a + // bad transition when it goes away. + behindDream = true; + } win.mLayoutNeeded = false; win.prelayout(); mPolicy.layoutWindowLw(win, win.mAttrs, null); @@ -8290,6 +8311,8 @@ public class WindowManagerService extends IWindowManager.Stub mAnimator.mUniverseBackground = universeBackground; } + boolean attachedBehindDream = false; + // Now perform layout of attached windows, which usually // depend on the position of the window they are attached to. // XXX does not deal with windows that are attached to windows @@ -8307,6 +8330,9 @@ public class WindowManagerService extends IWindowManager.Stub // if they want. (We do the normal layout for INVISIBLE // windows, since that means "perform layout as normal, // just don't display"). + if (attachedBehindDream && mPolicy.canBeForceHidden(win, win.mAttrs)) { + continue; + } if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled) || !win.mHaveFrame || win.mLayoutNeeded) { if (initial) { @@ -8322,6 +8348,11 @@ public class WindowManagerService extends IWindowManager.Stub + win.mContainingFrame + " mDisplayFrame=" + win.mDisplayFrame); } + } else if (win.mAttrs.type == TYPE_DREAM) { + // Don't layout windows behind a dream, so that if it + // does stuff like hide the status bar we won't get a + // bad transition when it goes away. + attachedBehindDream = behindDream; } } @@ -8690,7 +8721,7 @@ public class WindowManagerService extends IWindowManager.Stub private void updateResizingWindows(final WindowState w) { final WindowStateAnimator winAnimator = w.mWinAnimator; - if (w.mHasSurface && !w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) { + if (w.mHasSurface && w.mLayoutSeq == mLayoutSeq) { w.mContentInsetsChanged |= !w.mLastContentInsets.equals(w.mContentInsets); w.mVisibleInsetsChanged |= @@ -8784,6 +8815,7 @@ public class WindowManagerService extends IWindowManager.Stub final int type = attrs.type; if (canBeSeen && (type == TYPE_SYSTEM_DIALOG + || type == TYPE_RECENTS_OVERLAY || type == TYPE_KEYGUARD || type == TYPE_SYSTEM_ERROR)) { mInnerFields.mSyswin = true; @@ -9224,39 +9256,39 @@ public class WindowManagerService extends IWindowManager.Stub defaultDisplay.pendingLayoutChanges); } - if (!mResizingWindows.isEmpty()) { - for (i = mResizingWindows.size() - 1; i >= 0; i--) { - WindowState win = mResizingWindows.get(i); - final WindowStateAnimator winAnimator = win.mWinAnimator; - try { - if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, - "Reporting new frame to " + win + ": " + win.mCompatFrame); - int diff = 0; - boolean configChanged = win.isConfigChanged(); - if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION - // TODO: Remove once b/7094175 is fixed - || ((String)win.mAttrs.getTitle()).contains("Keyguard")) - && configChanged) { - Slog.i(TAG, "Sending new config to window " + win + ": " - + winAnimator.mSurfaceW + "x" + winAnimator.mSurfaceH - + " / " + mCurConfiguration + " / 0x" - + Integer.toHexString(diff)); - } - win.mConfiguration = mCurConfiguration; - if (DEBUG_ORIENTATION && - winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i( - TAG, "Resizing " + win + " WITH DRAW PENDING"); - win.mClient.resized(win.mFrame, win.mLastContentInsets, win.mLastVisibleInsets, - winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING, - configChanged ? win.mConfiguration : null); - win.mContentInsetsChanged = false; - win.mVisibleInsetsChanged = false; - winAnimator.mSurfaceResized = false; - } catch (RemoteException e) { - win.mOrientationChanging = false; - } + for (i = mResizingWindows.size() - 1; i >= 0; i--) { + WindowState win = mResizingWindows.get(i); + if (win.mAppFreezing) { + // Don't remove this window until rotation has completed. + continue; + } + final WindowStateAnimator winAnimator = win.mWinAnimator; + try { + if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, + "Reporting new frame to " + win + ": " + win.mCompatFrame); + int diff = 0; + boolean configChanged = win.isConfigChanged(); + if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) + && configChanged) { + Slog.i(TAG, "Sending new config to window " + win + ": " + + winAnimator.mSurfaceW + "x" + winAnimator.mSurfaceH + + " / " + mCurConfiguration + " / 0x" + + Integer.toHexString(diff)); + } + win.mConfiguration = mCurConfiguration; + if (DEBUG_ORIENTATION && + winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i( + TAG, "Resizing " + win + " WITH DRAW PENDING"); + win.mClient.resized(win.mFrame, win.mLastContentInsets, win.mLastVisibleInsets, + winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING, + configChanged ? win.mConfiguration : null); + win.mContentInsetsChanged = false; + win.mVisibleInsetsChanged = false; + winAnimator.mSurfaceResized = false; + } catch (RemoteException e) { + win.mOrientationChanging = false; } - mResizingWindows.clear(); + mResizingWindows.remove(i); } if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG, @@ -9450,18 +9482,22 @@ public class WindowManagerService extends IWindowManager.Stub } } - public void waitForWindowDrawn(IBinder token, IRemoteCallback callback) { - synchronized (mWindowMap) { - WindowState win = windowForClientLocked(null, token, true); - if (win != null) { - Pair<WindowState, IRemoteCallback> pair = - new Pair<WindowState, IRemoteCallback>(win, callback); - Message m = mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, pair); - mH.sendMessageDelayed(m, 2000); - mWaitingForDrawn.add(pair); - checkDrawnWindowsLocked(); + public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) { + if (token != null && callback != null) { + synchronized (mWindowMap) { + WindowState win = windowForClientLocked(null, token, true); + if (win != null) { + Pair<WindowState, IRemoteCallback> pair = + new Pair<WindowState, IRemoteCallback>(win, callback); + Message m = mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, pair); + mH.sendMessageDelayed(m, 2000); + mWaitingForDrawn.add(pair); + checkDrawnWindowsLocked(); + return true; + } } } + return false; } void setHoldScreenLocked(final Session newHoldScreen) { @@ -9475,10 +9511,10 @@ public class WindowManagerService extends IWindowManager.Stub final boolean state = mHoldingScreenWakeLock.isHeld(); if (hold != state) { if (hold) { - mPolicy.screenOnStartedLw(); mHoldingScreenWakeLock.acquire(); + mPolicy.keepScreenOnStartedLw(); } else { - mPolicy.screenOnStoppedLw(); + mPolicy.keepScreenOnStoppedLw(); mHoldingScreenWakeLock.release(); } } @@ -10155,8 +10191,8 @@ public class WindowManagerService extends IWindowManager.Stub return mPolicy.hasNavigationBar(); } - public void lockNow() { - mPolicy.lockNow(); + public void lockNow(Bundle options) { + mPolicy.lockNow(options); } void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) { diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index 9963d14..23892f6 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -22,9 +22,6 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import com.android.server.input.InputWindowHandle; @@ -36,7 +33,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.IBinder; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; @@ -263,7 +259,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { DisplayContent mDisplayContent; // UserId and appId of the owner. Don't display windows of non-current user. - final int mOwnerUid; + int mOwnerUid; + + /** When true this window can be displayed on screens owther than mOwnerUid's */ + private boolean mShowToOwnerOnly; WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState attachedWindow, int seq, WindowManager.LayoutParams a, @@ -654,6 +653,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { * surface, or we are in the process of running an exit animation * that will remove the surface, or its app token has been hidden. */ + @Override public boolean isVisibleLw() { final AppWindowToken atoken = mAppToken; return mHasSurface && mPolicyVisibility && !mAttachedHidden @@ -669,6 +669,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { * for this "hidden behind keyguard" state rather than overloading * mPolicyVisibility. Ungh. */ + @Override public boolean isVisibleOrBehindKeyguardLw() { if (mRootToken.waitingToShow && mService.mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { @@ -787,18 +788,21 @@ final class WindowState implements WindowManagerPolicy.WindowState { * Like isOnScreen, but returns false if the surface hasn't yet * been drawn. */ + @Override public boolean isDisplayedLw() { final AppWindowToken atoken = mAppToken; return isDrawnLw() && mPolicyVisibility && ((!mAttachedHidden && (atoken == null || !atoken.hiddenRequested)) - || mWinAnimator.mAnimating); + || mWinAnimator.mAnimating + || (atoken != null && atoken.mAppAnimator.animation != null)); } /** * Return true if this window (or a window it is attached to, but not * considering its app token) is currently animating. */ + @Override public boolean isAnimatingLw() { return mWinAnimator.mAnimation != null; } @@ -939,8 +943,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { } boolean showLw(boolean doAnimation, boolean requestAnim) { - if (isOtherUsersAppWindow()) { - Slog.w(TAG, "Current user " + mService.mCurrentUserId + " trying to display " + if (isHiddenFromUserLocked()) { + Slog.w(TAG, "current user violation " + mService.mCurrentUserId + " trying to display " + this + ", type " + mAttrs.type + ", belonging to " + mOwnerUid); return false; } @@ -1025,15 +1029,23 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mDisplayContent.isDefaultDisplay; } - boolean isOtherUsersAppWindow() { - final int type = mAttrs.type; - if ((UserHandle.getUserId(mOwnerUid) != mService.mCurrentUserId) - && (mOwnerUid != Process.SYSTEM_UID) - && (type >= TYPE_BASE_APPLICATION) && (type <= LAST_APPLICATION_WINDOW) - && (type != TYPE_APPLICATION_STARTING)) { - return true; + public void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) { + mShowToOwnerOnly = showToOwnerOnly; + } + + boolean isHiddenFromUserLocked() { + // Save some cycles by not calling getDisplayInfo unless it is an application + // window intended for all users. + if (mAttrs.type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW + && mAppToken != null && mAppToken.showWhenLocked) { + final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); + if (isFullscreen(displayInfo.appWidth, displayInfo.appHeight)) { + // Is a fullscreen window, like the clock alarm. Show to everyone. + return false; + } } - return false; + + return mShowToOwnerOnly && UserHandle.getUserId(mOwnerUid) != mService.mCurrentUserId; } private static void applyInsets(Region outRegion, Rect frame, Rect inset) { @@ -1072,6 +1084,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.print(prefix); pw.print("mDisplayId="); pw.print(mDisplayContent.getDisplayId()); pw.print(" mSession="); pw.print(mSession); pw.print(" mClient="); pw.println(mClient.asBinder()); + pw.print(prefix); pw.print("mOwnerUid="); pw.print(mOwnerUid); + pw.print(" mShowToOwnerOnly="); pw.println(mShowToOwnerOnly); pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); pw.print(" h="); pw.print(mRequestedHeight); @@ -1219,7 +1233,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { mLastTitle = mAttrs.getTitle(); mWasPaused = mToken.paused; mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this)) - + " " + mLastTitle + " paused=" + mWasPaused + "}"; + + " u" + UserHandle.getUserId(mSession.mUid) + + " " + mLastTitle + (mWasPaused ? " PAUSED}" : "}"); } return mStringNameCache; } diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index acf452f..2bfefe1 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -1334,8 +1334,8 @@ class WindowStateAnimator { // This must be called while inside a transaction. boolean performShowLocked() { - if (mWin.isOtherUsersAppWindow()) { - Slog.w(TAG, "Current user " + mService.mCurrentUserId + " trying to display " + if (mWin.isHiddenFromUserLocked()) { + Slog.w(TAG, "current user violation " + mService.mCurrentUserId + " trying to display " + this + ", type " + mWin.mAttrs.type + ", belonging to " + mWin.mOwnerUid); return false; } @@ -1500,7 +1500,7 @@ class WindowStateAnimator { int attr = -1; Animation a = null; if (anim != 0) { - a = AnimationUtils.loadAnimation(mContext, anim); + a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null; } else { switch (transit) { case WindowManagerPolicy.TRANSIT_ENTER: |
