diff options
Diffstat (limited to 'services/java')
123 files changed, 19333 insertions, 12140 deletions
diff --git a/services/java/Android.mk b/services/java/Android.mk index e70a6c9..95b28d9 100644 --- a/services/java/Android.mk +++ b/services/java/Android.mk @@ -13,9 +13,6 @@ LOCAL_MODULE:= services LOCAL_JAVA_LIBRARIES := android.policy telephony-common -LOCAL_NO_EMMA_INSTRUMENT := true -LOCAL_NO_EMMA_COMPILE := true - include $(BUILD_JAVA_LIBRARY) include $(BUILD_DROIDDOC) diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 38f4554..8e341df 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -177,7 +177,7 @@ class AppWidgetService extends IAppWidgetService.Stub mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - onUserRemoved(intent.getIntExtra(Intent.EXTRA_USERID, -1)); + onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); } }, userFilter); } diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index f9c432b..539e561 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -36,24 +36,26 @@ import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.graphics.Point; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; -import android.os.UserId; +import android.os.UserHandle; +import android.util.AtomicFile; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.TypedValue; import android.util.Xml; +import android.view.Display; import android.view.WindowManager; import android.widget.RemoteViews; import com.android.internal.appwidget.IAppWidgetHost; -import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsAdapterConnection; import com.android.internal.widget.IRemoteViewsFactory; @@ -188,11 +190,12 @@ class AppWidgetServiceImpl { void computeMaximumWidgetBitmapMemory() { WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - int height = wm.getDefaultDisplay().getRawHeight(); - int width = wm.getDefaultDisplay().getRawWidth(); + Display display = wm.getDefaultDisplay(); + Point size = new Point(); + display.getRealSize(size); // Cap memory usage at 1.5 times the size of the display // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h - mMaxWidgetBitmapMemory = 6 * width * height; + mMaxWidgetBitmapMemory = 6 * size.x * size.y; } public void systemReady(boolean safeMode) { @@ -210,11 +213,19 @@ class AppWidgetServiceImpl { synchronized (mAppWidgetIds) { ensureStateLoadedLocked(); - int N = mInstalledProviders.size(); + // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the + // list of installed providers and skip providers that we don't need to update. + // Also note that remove the provider does not clear the Provider component data. + ArrayList<Provider> installedProviders = + new ArrayList<Provider>(mInstalledProviders); + HashSet<ComponentName> removedProviders = new HashSet<ComponentName>(); + int N = installedProviders.size(); for (int i = N - 1; i >= 0; i--) { - Provider p = mInstalledProviders.get(i); - String pkgName = p.info.provider.getPackageName(); - updateProvidersForPackageLocked(pkgName); + Provider p = installedProviders.get(i); + ComponentName cn = p.info.provider; + if (!removedProviders.contains(cn)) { + updateProvidersForPackageLocked(cn.getPackageName(), removedProviders); + } } saveStateLocked(); } @@ -225,6 +236,7 @@ class AppWidgetServiceImpl { final String action = intent.getAction(); boolean added = false; boolean changed = false; + boolean providersModified = false; String pkgList[] = null; if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); @@ -256,12 +268,12 @@ class AppWidgetServiceImpl { || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) { for (String pkgName : pkgList) { // The package was just upgraded - updateProvidersForPackageLocked(pkgName); + providersModified |= updateProvidersForPackageLocked(pkgName, null); } } else { // The package was just added for (String pkgName : pkgList) { - addProvidersForPackageLocked(pkgName); + providersModified |= addProvidersForPackageLocked(pkgName); } } saveStateLocked(); @@ -274,12 +286,20 @@ class AppWidgetServiceImpl { synchronized (mAppWidgetIds) { ensureStateLoadedLocked(); for (String pkgName : pkgList) { - removeProvidersForPackageLocked(pkgName); + providersModified |= removeProvidersForPackageLocked(pkgName); saveStateLocked(); } } } } + + if (providersModified) { + // If the set of providers has been modified, notify each active AppWidgetHost + synchronized (mAppWidgetIds) { + ensureStateLoadedLocked(); + notifyHostsForProvidersChangedLocked(); + } + } } private void dumpProvider(Provider p, int index, PrintWriter pw) { @@ -479,7 +499,7 @@ class AppWidgetServiceImpl { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED); intent.setComponent(p.info.provider); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); if (p.instances.size() == 0) { // cancel the future updates cancelBroadcasts(p); @@ -487,7 +507,7 @@ class AppWidgetServiceImpl { // send the broacast saying that the provider is not in use any more intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); } } } @@ -573,7 +593,7 @@ class AppWidgetServiceImpl { private boolean callerHasBindAppWidgetPermission(String packageName) { int callingUid = Binder.getCallingUid(); try { - if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) { + if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) { return false; } } catch (Exception e) { @@ -645,7 +665,7 @@ class AppWidgetServiceImpl { mBoundRemoteViewsServices.remove(key); } - int userId = UserId.getUserId(id.provider.uid); + int userId = UserHandle.getUserId(id.provider.uid); // Bind to the RemoteViewsService (which will trigger a callback to the // RemoteViewsAdapter.onServiceConnected()) final long token = Binder.clearCallingIdentity(); @@ -736,7 +756,7 @@ class AppWidgetServiceImpl { } }; - int userId = UserId.getUserId(id.provider.uid); + int userId = UserHandle.getUserId(id.provider.uid); // Bind to the service and remove the static intent->factory mapping in the // RemoteViewsService. final long token = Binder.clearCallingIdentity(); @@ -860,7 +880,7 @@ class AppWidgetServiceImpl { intent.setComponent(p.info.provider); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); } } @@ -1006,7 +1026,7 @@ class AppWidgetServiceImpl { } }; - int userId = UserId.getUserId(id.provider.uid); + int userId = UserHandle.getUserId(id.provider.uid); // Bind to the service and call onDataSetChanged() final long token = Binder.clearCallingIdentity(); try { @@ -1185,7 +1205,7 @@ class AppWidgetServiceImpl { void sendEnableIntentLocked(Provider p) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); } void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) { @@ -1193,7 +1213,7 @@ class AppWidgetServiceImpl { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); } } @@ -1355,7 +1375,7 @@ class AppWidgetServiceImpl { throw new IllegalArgumentException("packageName and uid don't match packageName=" + packageName); } - if (!UserId.isSameApp(callingUid, packageUid)) { + if (!UserHandle.isSameApp(callingUid, packageUid)) { throw new IllegalArgumentException("packageName and uid don't match packageName=" + packageName); } @@ -1642,7 +1662,8 @@ class AppWidgetServiceImpl { getSettingsFile(mUserId).delete(); } - void addProvidersForPackageLocked(String pkgName) { + boolean addProvidersForPackageLocked(String pkgName) { + boolean providersAdded = false; Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setPackage(pkgName); List<ResolveInfo> broadcastReceivers; @@ -1652,7 +1673,7 @@ class AppWidgetServiceImpl { PackageManager.GET_META_DATA, mUserId); } catch (RemoteException re) { // Shouldn't happen, local call - return; + return false; } final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); for (int i = 0; i < N; i++) { @@ -1663,11 +1684,21 @@ class AppWidgetServiceImpl { } if (pkgName.equals(ai.packageName)) { addProviderLocked(ri); + providersAdded = true; } } + + return providersAdded; } - void updateProvidersForPackageLocked(String pkgName) { + /** + * Updates all providers with the specified package names, and records any providers that were + * pruned. + * + * @return whether any providers were updated + */ + boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) { + boolean providersUpdated = false; HashSet<String> keep = new HashSet<String>(); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setPackage(pkgName); @@ -1678,7 +1709,7 @@ class AppWidgetServiceImpl { PackageManager.GET_META_DATA, mUserId); } catch (RemoteException re) { // Shouldn't happen, local call - return; + return false; } // add the missing ones and collect which ones to keep @@ -1695,6 +1726,7 @@ class AppWidgetServiceImpl { if (p == null) { if (addProviderLocked(ri)) { keep.add(ai.name); + providersUpdated = true; } } else { Provider parsed = parseProviderInfoXml(component, ri); @@ -1729,6 +1761,7 @@ class AppWidgetServiceImpl { } // Now that we've told the host, push out an update. sendUpdateIntentLocked(p, appWidgetIds); + providersUpdated = true; } } } @@ -1741,17 +1774,25 @@ class AppWidgetServiceImpl { Provider p = mInstalledProviders.get(i); if (pkgName.equals(p.info.provider.getPackageName()) && !keep.contains(p.info.provider.getClassName())) { + if (removedProviders != null) { + removedProviders.add(p.info.provider); + } removeProviderLocked(i, p); + providersUpdated = true; } } + + return providersUpdated; } - void removeProvidersForPackageLocked(String pkgName) { + boolean removeProvidersForPackageLocked(String pkgName) { + boolean providersRemoved = false; int N = mInstalledProviders.size(); for (int i = N - 1; i >= 0; i--) { Provider p = mInstalledProviders.get(i); if (pkgName.equals(p.info.provider.getPackageName())) { removeProviderLocked(i, p); + providersRemoved = true; } } @@ -1766,5 +1807,24 @@ class AppWidgetServiceImpl { deleteHostLocked(host); } } + + return providersRemoved; + } + + void notifyHostsForProvidersChangedLocked() { + final int N = mHosts.size(); + for (int i = N - 1; i >= 0; i--) { + Host host = mHosts.get(i); + try { + if (host.callbacks != null) { + host.callbacks.providersChanged(); + } + } catch (RemoteException ex) { + // It failed; remove the callback. No need to prune because + // we know that this host is still referenced by this + // instance. + host.callbacks = null; + } + } } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 2167c49..8be0ba8 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -65,6 +65,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.UserHandle; import android.os.WorkSource; import android.os.storage.IMountService; import android.provider.Settings; @@ -4845,6 +4846,18 @@ class BackupManagerService extends IBackupManager.Stub { // ----- IBackupManager binder interface ----- public void dataChanged(final String packageName) { + final int callingUserHandle = UserHandle.getCallingUserId(); + if (callingUserHandle != UserHandle.USER_OWNER) { + // App is running under a non-owner user profile. For now, we do not back + // up data from secondary user profiles. + // TODO: backups for all user profiles. + if (MORE_DEBUG) { + Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " + + callingUserHandle); + } + return; + } + final HashSet<String> targets = dataChangedTargets(packageName); if (targets == null) { Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" @@ -4937,6 +4950,11 @@ class BackupManagerService extends IBackupManager.Stub { boolean doAllApps, boolean includeSystem, String[] pkgList) { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); + final int callingUserHandle = UserHandle.getCallingUserId(); + if (callingUserHandle != UserHandle.USER_OWNER) { + throw new IllegalStateException("Backup supported only for the device owner"); + } + // Validate if (!doAllApps) { if (!includeShared) { @@ -5001,6 +5019,11 @@ class BackupManagerService extends IBackupManager.Stub { public void fullRestore(ParcelFileDescriptor fd) { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); + final int callingUserHandle = UserHandle.getCallingUserId(); + if (callingUserHandle != UserHandle.USER_OWNER) { + throw new IllegalStateException("Restore supported only for the device owner"); + } + long oldId = Binder.clearCallingIdentity(); try { diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index ab9ae69..4192a93 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -68,7 +68,7 @@ import java.util.Arrays; * a degree Centigrade</p> * <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p> */ -class BatteryService extends Binder { +public class BatteryService extends Binder { private static final String TAG = BatteryService.class.getSimpleName(); private static final boolean LOCAL_LOGV = false; @@ -93,6 +93,7 @@ class BatteryService extends Binder { private boolean mAcOnline; private boolean mUsbOnline; + private boolean mWirelessOnline; private int mBatteryStatus; private int mBatteryHealth; private boolean mBatteryPresent; @@ -148,12 +149,13 @@ class BatteryService extends Binder { update(); } - final boolean isPowered() { + public final boolean isPowered() { // assume we are powered if battery state is unknown so the "stay on while plugged in" option will work. - return (mAcOnline || mUsbOnline || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN); + return (mAcOnline || mUsbOnline || mWirelessOnline + || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN); } - final boolean isPowered(int plugTypeSet) { + public final boolean isPowered(int plugTypeSet) { // assume we are powered if battery state is unknown so // the "stay on while plugged in" option will work. if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { @@ -169,10 +171,13 @@ class BatteryService extends Binder { if (mUsbOnline) { plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB; } + if (mWirelessOnline) { + plugTypeBit |= BatteryManager.BATTERY_PLUGGED_WIRELESS; + } return (plugTypeSet & plugTypeBit) != 0; } - final int getPlugType() { + public final int getPlugType() { return mPlugType; } @@ -195,10 +200,15 @@ class BatteryService extends Binder { }; // returns battery level as a percentage - final int getBatteryLevel() { + public final int getBatteryLevel() { return mBatteryLevel; } + // true if battery level is below the first warning threshold + public final boolean isBatteryLow() { + return mBatteryPresent && mBatteryLevel <= mLowBatteryWarningLevel; + } + void systemReady() { // check our power situation now that it is safe to display the shutdown dialog. shutdownIfNoPower(); @@ -243,6 +253,8 @@ class BatteryService extends Binder { mPlugType = BatteryManager.BATTERY_PLUGGED_AC; } else if (mUsbOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_USB; + } else if (mWirelessOnline) { + mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; } else { mPlugType = BATTERY_PLUGGED_NONE; } @@ -398,6 +410,7 @@ class BatteryService extends Binder { " temperature: " + mBatteryTemperature + " technology: " + mBatteryTechnology + " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline + + " Wireless powered:" + mWirelessOnline + " icon:" + icon + " invalid charger:" + mInvalidCharger); } @@ -503,6 +516,7 @@ class BatteryService extends Binder { pw.println("Current Battery Service state:"); pw.println(" AC powered: " + mAcOnline); pw.println(" USB powered: " + mUsbOnline); + pw.println(" Wireless powered: " + mWirelessOnline); pw.println(" status: " + mBatteryStatus); pw.println(" health: " + mBatteryHealth); pw.println(" present: " + mBatteryPresent); @@ -523,6 +537,8 @@ class BatteryService extends Binder { mAcOnline = Integer.parseInt(value) != 0; } else if ("usb".equals(key)) { mUsbOnline = Integer.parseInt(value) != 0; + } else if ("wireless".equals(key)) { + mWirelessOnline = Integer.parseInt(value) != 0; } else if ("status".equals(key)) { mBatteryStatus = Integer.parseInt(value); } else if ("level".equals(key)) { @@ -603,4 +619,3 @@ class BatteryService extends Binder { } } } - diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java new file mode 100755 index 0000000..4c98ac3 --- /dev/null +++ b/services/java/com/android/server/BluetoothManagerService.java @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2012 Google Inc. + */ + +package com.android.server; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.IBluetoothManagerCallback; +import android.bluetooth.IBluetoothStateChangeCallback; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.Binder; +import android.provider.Settings; +import android.util.Log; +import java.util.List; +import java.util.ArrayList; +class BluetoothManagerService extends IBluetoothManager.Stub { + private static final String TAG = "BluetoothManagerService"; + private static final boolean DBG = true; + + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; + private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED"; + private static final String EXTRA_ACTION="action"; + private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; + private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind + private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save + + private static final int MESSAGE_ENABLE = 1; + private static final int MESSAGE_DISABLE = 2; + private static final int MESSAGE_REGISTER_ADAPTER = 20; + private static final int MESSAGE_UNREGISTER_ADAPTER = 21; + private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; + private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31; + private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; + private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41; + private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60; + private static final int MESSAGE_TIMEOUT_BIND =100; + private static final int MESSAGE_TIMEOUT_UNBIND =101; + private static final int MESSAGE_GET_NAME_AND_ADDRESS=200; + private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201; + private static final int MAX_SAVE_RETRIES=3; + + private final Context mContext; + + // Locks are not provided for mName and mAddress. + // They are accessed in handler or broadcast receiver, same thread context. + private String mAddress; + private String mName; + private final ContentResolver mContentResolver; + private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks; + private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks; + private IBluetooth mBluetooth; + private boolean mBinding; + private boolean mUnbinding; + private boolean mQuietEnable = false; + + private void registerForAirplaneMode(IntentFilter filter) { + final ContentResolver resolver = mContext.getContentResolver(); + final String airplaneModeRadios = Settings.System.getString(resolver, + Settings.System.AIRPLANE_MODE_RADIOS); + final String toggleableRadios = Settings.System.getString(resolver, + Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS); + boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true : + airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); + if (mIsAirplaneSensitive) { + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + } + } + + private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { + @Override + public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { + Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState); + mHandler.sendMessage(msg); + } + }; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { + String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); + if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName); + if (newName != null) { + storeNameAndAddress(newName, null); + } + } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { + if (isAirplaneModeOn()) { + // disable without persisting the setting + handleDisable(false); + } else { + if (isBluetoothPersistedStateOn()) { + // enable without persisting the setting + handleEnable(false, false); + } + } + } + } + }; + + BluetoothManagerService(Context context) { + mContext = context; + mBluetooth = null; + mBinding = false; + mUnbinding = false; + mAddress = null; + mName = null; + mContentResolver = context.getContentResolver(); + mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>(); + mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>(); + IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); + filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); + registerForAirplaneMode(filter); + mContext.registerReceiver(mReceiver, filter); + boolean airplaneModeOn = isAirplaneModeOn(); + boolean bluetoothOn = isBluetoothPersistedStateOn(); + loadStoredNameAndAddress(); + if (DBG) Log.d(TAG, "airplaneModeOn: " + airplaneModeOn + " bluetoothOn: " + bluetoothOn); + if (bluetoothOn) { + //Enable + if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); + enable(); + } else if (!isNameAndAddressSet()) { + //Sync the Bluetooth name and address from the Bluetooth Adapter + if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); + getNameAndAddress(); + } + } + + /** + * Returns true if airplane mode is currently on + */ + private final boolean isAirplaneModeOn() { + return Settings.System.getInt(mContext.getContentResolver(), + Settings.System.AIRPLANE_MODE_ON, 0) == 1; + } + + /** + * Returns true if the Bluetooth saved state is "on" + */ + private final boolean isBluetoothPersistedStateOn() { + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.BLUETOOTH_ON, 0) ==1; + } + + /** + * Save the Bluetooth on/off state + * + */ + private void persistBluetoothSetting(boolean setOn) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BLUETOOTH_ON, + setOn ? 1 : 0); + } + + /** + * Returns true if the Bluetooth Adapter's name and address is + * locally cached + * @return + */ + private boolean isNameAndAddressSet() { + return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0; + } + + /** + * Retrieve the Bluetooth Adapter's name and address and save it in + * in the local cache + */ + private void loadStoredNameAndAddress() { + if (DBG) Log.d(TAG, "Loading stored name and address"); + mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); + mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); + if (mName == null || mAddress == null) { + if (DBG) Log.d(TAG, "Name or address not cached..."); + } + } + + /** + * Save the Bluetooth name and address in the persistent store. + * Only non-null values will be saved. + * @param name + * @param address + */ + private void storeNameAndAddress(String name, String address) { + if (name != null) { + Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); + mName = name; + if (DBG) Log.d(TAG,"Stored Bluetooth name: " + + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME)); + } + + if (address != null) { + Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); + mAddress=address; + if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " + + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS)); + } + } + + public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); + msg.obj = callback; + mHandler.sendMessage(msg); + synchronized(mConnection) { + return mBluetooth; + } + } + + public void unregisterAdapter(IBluetoothManagerCallback callback) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); + msg.obj = callback; + mHandler.sendMessage(msg); + } + + public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); + msg.obj = callback; + mHandler.sendMessage(msg); + } + + public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); + msg.obj = callback; + mHandler.sendMessage(msg); + } + + public boolean isEnabled() { + synchronized(mConnection) { + try { + return (mBluetooth != null && mBluetooth.isEnabled()); + } catch (RemoteException e) { + Log.e(TAG, "isEnabled()", e); + } + } + return false; + } + + public void getNameAndAddress() { + if (DBG) { + Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth + + " mBinding = " + mBinding); + } + synchronized(mConnection) { + if (mBinding) return; + if (mConnection == null) mBinding = true; + } + Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); + mHandler.sendMessage(msg); + } + public boolean enableNoAutoConnect() + { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + if (DBG) { + Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding); + } + if (Binder.getCallingUid() != android.os.Process.NFC_UID) { + throw new SecurityException("no permission to enable Bluetooth quietly"); + } + synchronized(mConnection) { + if (mBinding) { + Log.w(TAG,"enableNoAutoConnect(): binding in progress. Returning.."); + return true; + } + if (mConnection == null) mBinding = true; + } + + Message msg = mHandler.obtainMessage(MESSAGE_ENABLE); + msg.arg1=0; //No persist + msg.arg2=1; //Quiet mode + mHandler.sendMessage(msg); + return true; + + } + public boolean enable() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + if (DBG) { + Log.d(TAG,"enable(): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding); + } + + synchronized(mConnection) { + if (mBinding) { + Log.w(TAG,"enable(): binding in progress. Returning.."); + return true; + } + if (mConnection == null) mBinding = true; + } + + Message msg = mHandler.obtainMessage(MESSAGE_ENABLE); + msg.arg1=1; //persist + msg.arg2=0; //No Quiet Mode + mHandler.sendMessage(msg); + return true; + } + + public boolean disable(boolean persist) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + if (DBG) { + Log.d(TAG,"disable(): mBluetooth = " + mBluetooth + + " mBinding = " + mBinding); + } + + synchronized(mConnection) { + if (mBluetooth == null) return false; + } + Message msg = mHandler.obtainMessage(MESSAGE_DISABLE); + msg.arg1=(persist?1:0); + mHandler.sendMessage(msg); + return true; + } + + public void unbindAndFinish() { + if (DBG) { + Log.d(TAG,"unbindAndFinish(): " + mBluetooth + + " mBinding = " + mBinding); + } + + synchronized (mConnection) { + if (mUnbinding) return; + mUnbinding = true; + if (mConnection != null) { + if (!mConnection.isGetNameAddressOnly()) { + //Unregister callback object + try { + mBluetooth.unregisterCallback(mBluetoothCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to register BluetoothCallback",re); + } + } + if (DBG) Log.d(TAG, "Sending unbind request."); + mBluetooth = null; + //Unbind + mContext.unbindService(mConnection); + mUnbinding = false; + } else { + mUnbinding=false; + } + } + } + + private void sendBluetoothStateCallback(boolean isUp) { + int n = mStateChangeCallbacks.beginBroadcast(); + if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); + for (int i=0; i <n;i++) { + try { + mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp); + } catch (RemoteException e) { + Log.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e); + } + } + mStateChangeCallbacks.finishBroadcast(); + } + + /** + * Inform BluetoothAdapter instances that Adapter service is down + */ + private void sendBluetoothServiceDownCallback() { + if (!mConnection.isGetNameAddressOnly()) { + if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks"); + int n = mCallbacks.beginBroadcast(); + Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers."); + for (int i=0; i <n;i++) { + try { + mCallbacks.getBroadcastItem(i).onBluetoothServiceDown(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e); + } + } + mCallbacks.finishBroadcast(); + } + } + public String getAddress() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + synchronized(mConnection) { + if (mBluetooth != null) { + try { + return mBluetooth.getAddress(); + } catch (RemoteException e) { + Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e); + } + } + } + // mAddress is accessed from outside. + // It is alright without a lock. Here, bluetooth is off, no other thread is + // changing mAddress + return mAddress; + } + + public String getName() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + synchronized(mConnection) { + if (mBluetooth != null) { + try { + return mBluetooth.getName(); + } catch (RemoteException e) { + Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e); + } + } + } + // mName is accessed from outside. + // It alright without a lock. Here, bluetooth is off, no other thread is + // changing mName + return mName; + } + + private class BluetoothServiceConnection implements ServiceConnection { + + private boolean mGetNameAddressOnly; + + public void setGetNameAddressOnly(boolean getOnly) { + mGetNameAddressOnly = getOnly; + } + + public boolean isGetNameAddressOnly() { + return mGetNameAddressOnly; + } + + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService"); + Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); + msg.obj = service; + mHandler.sendMessage(msg); + } + + public void onServiceDisconnected(ComponentName className) { + // Called if we unexpected disconnected. + if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService"); + Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); + mHandler.sendMessage(msg); + } + } + + private BluetoothServiceConnection mConnection = new BluetoothServiceConnection(); + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (DBG) Log.d (TAG, "Message: " + msg.what); + switch (msg.what) { + case MESSAGE_GET_NAME_AND_ADDRESS: { + if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS"); + synchronized(mConnection) { + //Start bind request + if (mBluetooth == null) { + if (DBG) Log.d(TAG, "Binding to service to get name and address"); + mConnection.setGetNameAddressOnly(true); + //Start bind timeout and bind + Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); + mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); + Intent i = new Intent(IBluetooth.class.getName()); + if (!mContext.bindService(i, mConnection, + Context.BIND_AUTO_CREATE)) { + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName()); + } + } + else { + Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS); + mHandler.sendMessage(saveMsg); + } + } + break; + } + case MESSAGE_SAVE_NAME_AND_ADDRESS: { + if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS"); + synchronized(mConnection) { + if (mBluetooth != null) { + String name = null; + String address = null; + try { + name = mBluetooth.getName(); + address = mBluetooth.getAddress(); + } catch (RemoteException re) { + Log.e(TAG,"",re); + } + + if (name != null && address != null) { + storeNameAndAddress(name,address); + sendBluetoothServiceDownCallback(); + unbindAndFinish(); + } else { + if (msg.arg1 < MAX_SAVE_RETRIES) { + Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS); + retryMsg.arg1= 1+msg.arg1; + if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1); + mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS); + } else { + Log.w(TAG,"Maximum name/address remote retrieval retry exceeded"); + sendBluetoothServiceDownCallback(); + unbindAndFinish(); + } + } + } + } + break; + } + case MESSAGE_ENABLE: + if (DBG) { + Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth); + } + + handleEnable(msg.arg1 == 1, msg.arg2 ==1); + break; + + case MESSAGE_DISABLE: + handleDisable(msg.arg1 == 1); + break; + + case MESSAGE_REGISTER_ADAPTER: + { + IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; + boolean added = mCallbacks.register(callback); + Log.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added ); + } + break; + case MESSAGE_UNREGISTER_ADAPTER: + { + IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; + boolean removed = mCallbacks.unregister(callback); + Log.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed); + break; + } + case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: + { + IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; + mStateChangeCallbacks.register(callback); + break; + } + case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: + { + IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; + mStateChangeCallbacks.unregister(callback); + break; + } + case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: + { + if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED"); + + //Remove timeout + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + + IBinder service = (IBinder) msg.obj; + synchronized(mConnection) { + mBinding = false; + mBluetooth = IBluetooth.Stub.asInterface(service); + + if (mConnection.isGetNameAddressOnly()) { + //Request GET NAME AND ADDRESS + Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); + mHandler.sendMessage(getMsg); + return; + } + + //Register callback object + try { + mBluetooth.registerCallback(mBluetoothCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to register BluetoothCallback",re); + } + + //Inform BluetoothAdapter instances that service is up + int n = mCallbacks.beginBroadcast(); + Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers."); + for (int i=0; i <n;i++) { + try { + mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth); + } catch (RemoteException e) { + Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e); + } + } + mCallbacks.finishBroadcast(); + + //Do enable request + try { + if (mQuietEnable == false) { + if(!mBluetooth.enable()) { + Log.e(TAG,"IBluetooth.enable() returned false"); + } + } + else + { + if(!mBluetooth.enableNoAutoConnect()) { + Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false"); + } + } + } catch (RemoteException e) { + Log.e(TAG,"Unable to call enable()",e); + } + } + break; + } + case MESSAGE_TIMEOUT_BIND: { + Log.e(TAG, "MESSAGE_TIMEOUT_BIND"); + synchronized(mConnection) { + mBinding = false; + } + break; + } + case MESSAGE_BLUETOOTH_STATE_CHANGE: + { + int prevState = msg.arg1; + int newState = msg.arg2; + if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState); + if (prevState != newState) { + //Notify all proxy objects first of adapter state change + if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) { + boolean isUp = (newState==BluetoothAdapter.STATE_ON); + sendBluetoothStateCallback(isUp); + + //If Bluetooth is off, send service down event to proxy objects, and unbind + if (!isUp) { + sendBluetoothServiceDownCallback(); + unbindAndFinish(); + } + } + + //Send broadcast message to everyone else + Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState); + mContext.sendBroadcast(intent,BLUETOOTH_PERM); + } + break; + } + case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: + { + if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED"); + sendBluetoothServiceDownCallback(); + break; + } + case MESSAGE_TIMEOUT_UNBIND: + { + Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); + synchronized(mConnection) { + mUnbinding = false; + } + break; + } + } + } + }; + + private void handleEnable(boolean persist, boolean quietMode) { + if (persist) { + persistBluetoothSetting(true); + } + + mQuietEnable = quietMode; + + synchronized(mConnection) { + if (mBluetooth == null) { + //Start bind timeout and bind + Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); + 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)) { + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName()); + } + } else { + //Check if name and address is loaded if not get it first. + if (!isNameAndAddressSet()) { + try { + if (DBG) Log.d(TAG,"Getting and storing Bluetooth name and address prior to enable."); + storeNameAndAddress(mBluetooth.getName(),mBluetooth.getAddress()); + } catch (RemoteException e) {Log.e(TAG, "", e);}; + } + + //Enable bluetooth + try { + if (!mQuietEnable) { + if(!mBluetooth.enable()) { + Log.e(TAG,"IBluetooth.enable() returned false"); + } + } + else { + if(!mBluetooth.enableNoAutoConnect()) { + Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false"); + } + } + } catch (RemoteException e) { + Log.e(TAG,"Unable to call enable()",e); + } + } + } + } + + private void handleDisable(boolean persist) { + synchronized(mConnection) { + if (mBluetooth != null ) { + if (persist) { + persistBluetoothSetting(false); + } + mConnection.setGetNameAddressOnly(false); + if (DBG) Log.d(TAG,"Sending off request."); + + try { + if(!mBluetooth.disable()) { + Log.e(TAG,"IBluetooth.disable() returned false"); + } + } catch (RemoteException e) { + Log.e(TAG,"Unable to call disable()",e); + } + } + } + } +} diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java index 8a6a550..0bf7aad 100644 --- a/services/java/com/android/server/ClipboardService.java +++ b/services/java/com/android/server/ClipboardService.java @@ -36,7 +36,7 @@ import android.os.Parcel; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.UserId; +import android.os.UserHandle; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -96,7 +96,7 @@ public class ClipboardService extends IClipboard.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_USER_REMOVED.equals(action)) { - removeClipboard(intent.getIntExtra(Intent.EXTRA_USERID, 0)); + removeClipboard(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } } }, userFilter); @@ -115,7 +115,7 @@ public class ClipboardService extends IClipboard.Stub { } private PerUserClipboard getClipboard() { - return getClipboard(UserId.getCallingUserId()); + return getClipboard(UserHandle.getCallingUserId()); } private PerUserClipboard getClipboard(int userId) { @@ -258,7 +258,7 @@ public class ClipboardService extends IClipboard.Stub { PackageInfo pi; try { pi = mPm.getPackageInfo(pkg, 0); - if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) { + if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) { throw new SecurityException("Calling uid " + uid + " does not own package " + pkg); } diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java index 9a25d2e..c316733 100644 --- a/services/java/com/android/server/CommonTimeManagementService.java +++ b/services/java/com/android/server/CommonTimeManagementService.java @@ -120,6 +120,8 @@ class CommonTimeManagementService extends Binder { reevaluateServiceState(); } public void limitReached(String limitName, String iface) { } + + public void interfaceClassDataActivityChanged(String label, boolean active) {} }; private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 86ada40..776a1a4 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -16,18 +16,29 @@ package com.android.server; +import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; +import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; +import static android.net.ConnectivityManager.TYPE_BLUETOOTH; +import static android.net.ConnectivityManager.TYPE_DUMMY; +import static android.net.ConnectivityManager.TYPE_ETHERNET; +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.ConnectivityManager.TYPE_WIMAX; +import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import android.bluetooth.BluetoothTetheringDataTracker; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; @@ -35,6 +46,7 @@ import android.net.ConnectivityManager; import android.net.DummyDataStateTracker; import android.net.EthernetDataTracker; import android.net.IConnectivityManager; +import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; @@ -69,6 +81,8 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; +import android.security.Credentials; +import android.security.KeyStore; import android.text.TextUtils; import android.util.EventLog; import android.util.Slog; @@ -76,21 +90,23 @@ import android.util.SparseIntArray; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; +import com.android.server.net.BaseNetworkObserver; +import com.android.server.net.LockdownVpnTracker; import com.google.android.collect.Lists; import com.google.android.collect.Sets; + import dalvik.system.DexClassLoader; + import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.InvocationTargetException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -106,13 +122,15 @@ import java.util.List; * @hide */ public class ConnectivityService extends IConnectivityManager.Stub { + private static final String TAG = "ConnectivityService"; private static final boolean DBG = true; private static final boolean VDBG = false; - private static final String TAG = "ConnectivityService"; private static final boolean LOGD_RULES = false; + // TODO: create better separation between radio types and network types + // how long to wait before switching back to a radio's default network private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000; // system property that can override the above value @@ -126,7 +144,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { private Tethering mTethering; private boolean mTetheringConfigValid = false; + private KeyStore mKeyStore; + private Vpn mVpn; + private VpnCallback mVpnCallback = new VpnCallback(); + + private boolean mLockdownEnabled; + private LockdownVpnTracker mLockdownTracker; /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */ private Object mRulesLock = new Object(); @@ -186,95 +210,83 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final boolean TO_DEFAULT_TABLE = true; private static final boolean TO_SECONDARY_TABLE = false; - // Share the event space with NetworkStateTracker (which can't see this - // internal class but sends us events). If you change these, change - // NetworkStateTracker.java too. - private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1; - private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100; - /** * used internally as a delayed event to make us switch back to the * default network */ - private static final int EVENT_RESTORE_DEFAULT_NETWORK = - MAX_NETWORK_STATE_TRACKER_EVENT + 1; + private static final int EVENT_RESTORE_DEFAULT_NETWORK = 1; /** * used internally to change our mobile data enabled flag */ - private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = - MAX_NETWORK_STATE_TRACKER_EVENT + 2; + private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2; /** * used internally to change our network preference setting * arg1 = networkType to prefer */ - private static final int EVENT_SET_NETWORK_PREFERENCE = - MAX_NETWORK_STATE_TRACKER_EVENT + 3; + private static final int EVENT_SET_NETWORK_PREFERENCE = 3; /** * used internally to synchronize inet condition reports * arg1 = networkType * arg2 = condition (0 bad, 100 good) */ - private static final int EVENT_INET_CONDITION_CHANGE = - MAX_NETWORK_STATE_TRACKER_EVENT + 4; + private static final int EVENT_INET_CONDITION_CHANGE = 4; /** * used internally to mark the end of inet condition hold periods * arg1 = networkType */ - private static final int EVENT_INET_CONDITION_HOLD_END = - MAX_NETWORK_STATE_TRACKER_EVENT + 5; + private static final int EVENT_INET_CONDITION_HOLD_END = 5; /** * used internally to set enable/disable cellular data * arg1 = ENBALED or DISABLED */ - private static final int EVENT_SET_MOBILE_DATA = - MAX_NETWORK_STATE_TRACKER_EVENT + 7; + private static final int EVENT_SET_MOBILE_DATA = 7; /** * used internally to clear a wakelock when transitioning * from one net to another */ - private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = - MAX_NETWORK_STATE_TRACKER_EVENT + 8; + private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8; /** * used internally to reload global proxy settings */ - private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = - MAX_NETWORK_STATE_TRACKER_EVENT + 9; + private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = 9; /** * used internally to set external dependency met/unmet * arg1 = ENABLED (met) or DISABLED (unmet) * arg2 = NetworkType */ - private static final int EVENT_SET_DEPENDENCY_MET = - MAX_NETWORK_STATE_TRACKER_EVENT + 10; + private static final int EVENT_SET_DEPENDENCY_MET = 10; /** * used internally to restore DNS properties back to the * default network */ - private static final int EVENT_RESTORE_DNS = - MAX_NETWORK_STATE_TRACKER_EVENT + 11; + private static final int EVENT_RESTORE_DNS = 11; /** * used internally to send a sticky broadcast delayed. */ - private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = - MAX_NETWORK_STATE_TRACKER_EVENT + 12; + private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 12; /** * Used internally to * {@link NetworkStateTracker#setPolicyDataEnable(boolean)}. */ - private static final int EVENT_SET_POLICY_DATA_ENABLE = MAX_NETWORK_STATE_TRACKER_EVENT + 13; + private static final int EVENT_SET_POLICY_DATA_ENABLE = 13; + + private static final int EVENT_VPN_STATE_CHANGED = 14; - private Handler mHandler; + /** Handler used for internal events. */ + private InternalHandler mHandler; + /** Handler used for incoming {@link NetworkStateTracker} events. */ + private NetworkStateTrackerHandler mTrackerHandler; // list of DeathRecipients used to make sure features are turned off when // a process dies @@ -328,11 +340,24 @@ public class ConnectivityService extends IConnectivityManager.Stub { public ConnectivityService(Context context, INetworkManagementService netd, INetworkStatsService statsService, INetworkPolicyManager policyManager) { + // Currently, omitting a NetworkFactory will create one internally + // TODO: create here when we have cleaner WiMAX support + this(context, netd, statsService, policyManager, null); + } + + public ConnectivityService(Context context, INetworkManagementService netManager, + INetworkStatsService statsService, INetworkPolicyManager policyManager, + NetworkFactory netFactory) { if (DBG) log("ConnectivityService starting up"); HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread"); handlerThread.start(); - mHandler = new MyHandler(handlerThread.getLooper()); + mHandler = new InternalHandler(handlerThread.getLooper()); + mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper()); + + if (netFactory == null) { + netFactory = new DefaultNetworkFactory(context, mTrackerHandler); + } // setup our unique device name if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) { @@ -358,8 +383,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } mContext = checkNotNull(context, "missing Context"); - mNetd = checkNotNull(netd, "missing INetworkManagementService"); + mNetd = checkNotNull(netManager, "missing INetworkManagementService"); mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager"); + mKeyStore = KeyStore.getInstance(); try { mPolicyManager.registerListener(mPolicyListener); @@ -472,69 +498,38 @@ public class ConnectivityService extends IConnectivityManager.Stub { mTestMode = SystemProperties.get("cm.test.mode").equals("true") && SystemProperties.get("ro.build.type").equals("eng"); - /* - * Create the network state trackers for Wi-Fi and mobile - * data. Maybe this could be done with a factory class, - * but it's not clear that it's worth it, given that - * the number of different network types is not going - * to change very often. - */ - for (int netType : mPriorityList) { - switch (mNetConfigs[netType].radio) { - case ConnectivityManager.TYPE_WIFI: - mNetTrackers[netType] = new WifiStateTracker(netType, - mNetConfigs[netType].name); - mNetTrackers[netType].startMonitoring(context, mHandler); - break; - case ConnectivityManager.TYPE_MOBILE: - mNetTrackers[netType] = new MobileDataStateTracker(netType, - mNetConfigs[netType].name); - mNetTrackers[netType].startMonitoring(context, mHandler); - break; - case ConnectivityManager.TYPE_DUMMY: - mNetTrackers[netType] = new DummyDataStateTracker(netType, - mNetConfigs[netType].name); - mNetTrackers[netType].startMonitoring(context, mHandler); - break; - case ConnectivityManager.TYPE_BLUETOOTH: - mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance(); - mNetTrackers[netType].startMonitoring(context, mHandler); - break; - case ConnectivityManager.TYPE_WIMAX: - mNetTrackers[netType] = makeWimaxStateTracker(); - if (mNetTrackers[netType]!= null) { - mNetTrackers[netType].startMonitoring(context, mHandler); - } - break; - case ConnectivityManager.TYPE_ETHERNET: - mNetTrackers[netType] = EthernetDataTracker.getInstance(); - mNetTrackers[netType].startMonitoring(context, mHandler); - break; - default: - loge("Trying to create a DataStateTracker for an unknown radio type " + - mNetConfigs[netType].radio); + + // Create and start trackers for hard-coded networks + for (int targetNetworkType : mPriorityList) { + final NetworkConfig config = mNetConfigs[targetNetworkType]; + final NetworkStateTracker tracker; + try { + tracker = netFactory.createTracker(targetNetworkType, config); + mNetTrackers[targetNetworkType] = tracker; + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Problem creating " + getNetworkTypeName(targetNetworkType) + + " tracker: " + e); continue; } - mCurrentLinkProperties[netType] = null; - if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) { - mNetTrackers[netType].reconnect(); + + tracker.startMonitoring(context, mTrackerHandler); + if (config.isDefault()) { + tracker.reconnect(); } } - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b); - - mTethering = new Tethering(mContext, nmService, statsService, this, mHandler.getLooper()); + mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper()); mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 || mTethering.getTetherableWifiRegexs().length != 0 || mTethering.getTetherableBluetoothRegexs().length != 0) && mTethering.getUpstreamIfaceTypes().length != 0); - mVpn = new Vpn(mContext, new VpnCallback()); + mVpn = new Vpn(mContext, mVpnCallback, mNetd); + mVpn.startMonitoring(mContext, mTrackerHandler); try { - nmService.registerObserver(mTethering); - nmService.registerObserver(mVpn); + mNetd.registerObserver(mTethering); + mNetd.registerObserver(mDataActivityObserver); } catch (RemoteException e) { loge("Error registering observer :" + e); } @@ -548,8 +543,55 @@ public class ConnectivityService extends IConnectivityManager.Stub { loadGlobalProxy(); } -private NetworkStateTracker makeWimaxStateTracker() { - //Initialize Wimax + + /** + * Factory that creates {@link NetworkStateTracker} instances using given + * {@link NetworkConfig}. + */ + public interface NetworkFactory { + public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config); + } + + private static class DefaultNetworkFactory implements NetworkFactory { + private final Context mContext; + private final Handler mTrackerHandler; + + public DefaultNetworkFactory(Context context, Handler trackerHandler) { + mContext = context; + mTrackerHandler = trackerHandler; + } + + @Override + public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) { + switch (config.radio) { + case TYPE_WIFI: + return new WifiStateTracker(targetNetworkType, config.name); + case TYPE_MOBILE: + return new MobileDataStateTracker(targetNetworkType, config.name); + case TYPE_DUMMY: + return new DummyDataStateTracker(targetNetworkType, config.name); + case TYPE_BLUETOOTH: + return BluetoothTetheringDataTracker.getInstance(); + case TYPE_WIMAX: + return makeWimaxStateTracker(mContext, mTrackerHandler); + case TYPE_ETHERNET: + return EthernetDataTracker.getInstance(); + default: + throw new IllegalArgumentException( + "Trying to create a NetworkStateTracker for an unknown radio type: " + + config.radio); + } + } + } + + /** + * Loads external WiMAX library and registers as system service, returning a + * {@link NetworkStateTracker} for WiMAX. Caller is still responsible for + * invoking {@link NetworkStateTracker#startMonitoring(Context, Handler)}. + */ + private static NetworkStateTracker makeWimaxStateTracker( + Context context, Handler trackerHandler) { + // Initialize Wimax DexClassLoader wimaxClassLoader; Class wimaxStateTrackerClass = null; Class wimaxServiceClass = null; @@ -562,25 +604,25 @@ private NetworkStateTracker makeWimaxStateTracker() { NetworkStateTracker wimaxStateTracker = null; - boolean isWimaxEnabled = mContext.getResources().getBoolean( + boolean isWimaxEnabled = context.getResources().getBoolean( com.android.internal.R.bool.config_wimaxEnabled); if (isWimaxEnabled) { try { - wimaxJarLocation = mContext.getResources().getString( + wimaxJarLocation = context.getResources().getString( com.android.internal.R.string.config_wimaxServiceJarLocation); - wimaxLibLocation = mContext.getResources().getString( + wimaxLibLocation = context.getResources().getString( com.android.internal.R.string.config_wimaxNativeLibLocation); - wimaxManagerClassName = mContext.getResources().getString( + wimaxManagerClassName = context.getResources().getString( com.android.internal.R.string.config_wimaxManagerClassname); - wimaxServiceClassName = mContext.getResources().getString( + wimaxServiceClassName = context.getResources().getString( com.android.internal.R.string.config_wimaxServiceClassname); - wimaxStateTrackerClassName = mContext.getResources().getString( + wimaxStateTrackerClassName = context.getResources().getString( com.android.internal.R.string.config_wimaxStateTrackerClassname); log("wimaxJarLocation: " + wimaxJarLocation); wimaxClassLoader = new DexClassLoader(wimaxJarLocation, - new ContextWrapper(mContext).getCacheDir().getAbsolutePath(), + new ContextWrapper(context).getCacheDir().getAbsolutePath(), wimaxLibLocation, ClassLoader.getSystemClassLoader()); try { @@ -601,13 +643,13 @@ private NetworkStateTracker makeWimaxStateTracker() { Constructor wmxStTrkrConst = wimaxStateTrackerClass.getConstructor (new Class[] {Context.class, Handler.class}); - wimaxStateTracker = (NetworkStateTracker)wmxStTrkrConst.newInstance(mContext, - mHandler); + wimaxStateTracker = (NetworkStateTracker) wmxStTrkrConst.newInstance( + context, trackerHandler); Constructor wmxSrvConst = wimaxServiceClass.getDeclaredConstructor (new Class[] {Context.class, wimaxStateTrackerClass}); wmxSrvConst.setAccessible(true); - IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(mContext, wimaxStateTracker); + IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(context, wimaxStateTracker); wmxSrvConst.setAccessible(false); ServiceManager.addService(WimaxManagerConstants.WIMAX_SERVICE, svcInvoker); @@ -623,6 +665,7 @@ private NetworkStateTracker makeWimaxStateTracker() { return wimaxStateTracker; } + /** * Sets the preferred network. * @param preference the new preference @@ -630,7 +673,8 @@ private NetworkStateTracker makeWimaxStateTracker() { public void setNetworkPreference(int preference) { enforceChangePermission(); - mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0)); + mHandler.sendMessage( + mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0)); } public int getNetworkPreference() { @@ -749,6 +793,9 @@ private NetworkStateTracker makeWimaxStateTracker() { info = new NetworkInfo(info); info.setDetailedState(DetailedState.BLOCKED, null, null); } + if (mLockdownTracker != null) { + info = mLockdownTracker.augmentNetworkInfo(info); + } return info; } @@ -766,6 +813,17 @@ private NetworkStateTracker makeWimaxStateTracker() { return getNetworkInfo(mActiveDefaultNetwork, uid); } + public NetworkInfo getActiveNetworkInfoUnfiltered() { + enforceAccessPermission(); + if (isNetworkTypeValid(mActiveDefaultNetwork)) { + final NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork]; + if (tracker != null) { + return tracker.getNetworkInfo(); + } + } + return null; + } + @Override public NetworkInfo getActiveNetworkInfoForUid(int uid) { enforceConnectivityInternalPermission(); @@ -923,6 +981,14 @@ private NetworkStateTracker makeWimaxStateTracker() { return tracker != null && tracker.setRadio(turnOn); } + private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() { + @Override + public void interfaceClassDataActivityChanged(String label, boolean active) { + int deviceType = Integer.parseInt(label); + sendDataActivityBroadcast(deviceType, active); + } + }; + /** * Used to notice when the calling process dies so we can self-expire * @@ -1017,6 +1083,12 @@ private NetworkStateTracker makeWimaxStateTracker() { // TODO - move this into individual networktrackers int usedNetworkType = convertFeatureToNetworkType(networkType, feature); + if (mLockdownEnabled) { + // Since carrier APNs usually aren't available from VPN + // endpoint, mark them as unavailable. + return PhoneConstants.APN_TYPE_NOT_AVAILABLE; + } + if (mProtectedNetworks.contains(usedNetworkType)) { enforceConnectivityInternalPermission(); } @@ -1591,6 +1663,10 @@ private NetworkStateTracker makeWimaxStateTracker() { int prevNetType = info.getType(); mNetTrackers[prevNetType].setTeardownRequested(false); + + // Remove idletimer previously setup in {@code handleConnect} + removeDataActivityTracking(prevNetType); + /* * If the disconnected network is not the active one, then don't report * this as a loss of connectivity. What probably happened is that we're @@ -1610,6 +1686,7 @@ private NetworkStateTracker makeWimaxStateTracker() { Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); if (info.isFailover()) { intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); info.setFailover(false); @@ -1719,7 +1796,7 @@ private NetworkStateTracker makeWimaxStateTracker() { } } - private void sendConnectedBroadcast(NetworkInfo info) { + public void sendConnectedBroadcast(NetworkInfo info) { sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE); sendGeneralBroadcast(info, CONNECTIVITY_ACTION); } @@ -1734,8 +1811,13 @@ private NetworkStateTracker makeWimaxStateTracker() { } private Intent makeGeneralIntent(NetworkInfo info, String bcastType) { + if (mLockdownTracker != null) { + info = mLockdownTracker.augmentNetworkInfo(info); + } + Intent intent = new Intent(bcastType); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); if (info.isFailover()) { intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); info.setFailover(false); @@ -1759,6 +1841,13 @@ private NetworkStateTracker makeWimaxStateTracker() { sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs); } + private void sendDataActivityBroadcast(int deviceType, boolean active) { + Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE); + intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType); + intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active); + mContext.sendOrderedBroadcast(intent, RECEIVE_DATA_ACTIVITY_CHANGE); + } + /** * Called when an attempt to fail over to another network has failed. * @param info the {@link NetworkInfo} for the failed network @@ -1779,6 +1868,7 @@ private NetworkStateTracker makeWimaxStateTracker() { Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); if (getActiveNetworkInfo() == null) { intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); } @@ -1856,14 +1946,35 @@ private NetworkStateTracker makeWimaxStateTracker() { } // load the global proxy at startup mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY)); + + // Try bringing up tracker, but if KeyStore isn't ready yet, wait + // for user to unlock device. + if (!updateLockdownVpn()) { + final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT); + mContext.registerReceiver(mUserPresentReceiver, filter); + } } + private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Try creating lockdown tracker, since user present usually means + // unlocked keystore. + if (updateLockdownVpn()) { + mContext.unregisterReceiver(this); + } + } + }; + private void handleConnect(NetworkInfo info) { final int type = info.getType(); + setupDataActivityTracking(type); + // snapshot isFailover, because sendConnectedBroadcast() resets it boolean isFailover = info.isFailover(); final NetworkStateTracker thisNet = mNetTrackers[type]; + final String thisIface = thisNet.getLinkProperties().getInterfaceName(); // if this is a default net and other default is running // kill the one not preferred @@ -1922,10 +2033,9 @@ private NetworkStateTracker makeWimaxStateTracker() { sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay()); // notify battery stats service about this network - final String iface = thisNet.getLinkProperties().getInterfaceName(); - if (iface != null) { + if (thisIface != null) { try { - BatteryStatsService.getService().noteNetworkInterfaceType(iface, type); + BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, type); } catch (RemoteException e) { // ignored; service lives in system_server } @@ -1933,6 +2043,58 @@ private NetworkStateTracker makeWimaxStateTracker() { } /** + * Setup data activity tracking for the given network interface. + * + * Every {@code setupDataActivityTracking} should be paired with a + * {@link removeDataActivityTracking} for cleanup. + */ + private void setupDataActivityTracking(int type) { + final NetworkStateTracker thisNet = mNetTrackers[type]; + final String iface = thisNet.getLinkProperties().getInterfaceName(); + + final int timeout; + + if (ConnectivityManager.isNetworkTypeMobile(type)) { + timeout = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.DATA_ACTIVITY_TIMEOUT_MOBILE, + 0); + // Canonicalize mobile network type + type = ConnectivityManager.TYPE_MOBILE; + } else if (ConnectivityManager.TYPE_WIFI == type) { + timeout = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.DATA_ACTIVITY_TIMEOUT_WIFI, + 0); + } else { + // do not track any other networks + timeout = 0; + } + + if (timeout > 0 && iface != null) { + try { + mNetd.addIdleTimer(iface, timeout, Integer.toString(type)); + } catch (RemoteException e) { + } + } + } + + /** + * Remove data activity tracking when network disconnects. + */ + private void removeDataActivityTracking(int type) { + final NetworkStateTracker net = mNetTrackers[type]; + final String iface = net.getLinkProperties().getInterfaceName(); + + if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) || + ConnectivityManager.TYPE_WIFI == type)) { + try { + // the call fails silently if no idletimer setup for this interface + mNetd.removeIdleTimer(iface); + } catch (RemoteException e) { + } + } + } + + /** * After a change in the connectivity state of a network. We're mainly * concerned with making sure that the list of DNS servers is set up * according to which networks are connected, and ensuring that the @@ -2136,9 +2298,9 @@ private NetworkStateTracker makeWimaxStateTracker() { */ public void updateNetworkSettings(NetworkStateTracker nt) { String key = nt.getTcpBufferSizesPropName(); - String bufferSizes = SystemProperties.get(key); + String bufferSizes = key == null ? null : SystemProperties.get(key); - if (bufferSizes.length() == 0) { + if (TextUtils.isEmpty(bufferSizes)) { if (VDBG) log(key + " not found in system properties. Using defaults"); // Setting to default values so we won't be stuck to previous values @@ -2431,8 +2593,8 @@ private NetworkStateTracker makeWimaxStateTracker() { } // must be stateless - things change under us. - private class MyHandler extends Handler { - public MyHandler(Looper looper) { + private class NetworkStateTrackerHandler extends Handler { + public NetworkStateTrackerHandler(Looper looper) { super(looper); } @@ -2482,6 +2644,9 @@ private NetworkStateTracker makeWimaxStateTracker() { } else if (state == NetworkInfo.State.CONNECTED) { handleConnect(info); } + if (mLockdownTracker != null) { + mLockdownTracker.onNetworkInfoChanged(info); + } break; case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: info = (NetworkInfo) msg.obj; @@ -2495,6 +2660,19 @@ private NetworkStateTracker makeWimaxStateTracker() { type = info.getType(); updateNetworkSettings(mNetTrackers[type]); break; + } + } + } + + private class InternalHandler extends Handler { + public InternalHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + NetworkInfo info; + switch (msg.what) { case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: String causedBy = null; synchronized (ConnectivityService.this) { @@ -2566,6 +2744,13 @@ private NetworkStateTracker makeWimaxStateTracker() { final int networkType = msg.arg1; final boolean enabled = msg.arg2 == ENABLED; handleSetPolicyDataEnable(networkType, enabled); + break; + } + case EVENT_VPN_STATE_CHANGED: { + if (mLockdownTracker != null) { + mLockdownTracker.onVpnStateChanged((NetworkInfo) msg.obj); + } + break; } } } @@ -2907,11 +3092,11 @@ private NetworkStateTracker makeWimaxStateTracker() { } } - private void log(String s) { + private static void log(String s) { Slog.d(TAG, s); } - private void loge(String s) { + private static void loge(String s) { Slog.e(TAG, s); } @@ -2964,6 +3149,7 @@ private NetworkStateTracker makeWimaxStateTracker() { */ @Override public boolean protectVpn(ParcelFileDescriptor socket) { + throwIfLockdownEnabled(); try { int type = mActiveDefaultNetwork; if (ConnectivityManager.isNetworkTypeValid(type)) { @@ -2990,6 +3176,7 @@ private NetworkStateTracker makeWimaxStateTracker() { */ @Override public boolean prepareVpn(String oldPackage, String newPackage) { + throwIfLockdownEnabled(); return mVpn.prepare(oldPackage, newPackage); } @@ -3002,18 +3189,22 @@ private NetworkStateTracker makeWimaxStateTracker() { */ @Override public ParcelFileDescriptor establishVpn(VpnConfig config) { + throwIfLockdownEnabled(); return mVpn.establish(config); } /** - * Start legacy VPN and return an intent to VpnDialogs. This method is - * used by VpnSettings and not available in ConnectivityManager. - * Permissions are checked in Vpn class. - * @hide + * Start legacy VPN, controlling native daemons as needed. Creates a + * secondary thread to perform connection work, returning quickly. */ @Override - public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { - mVpn.startLegacyVpn(config, racoon, mtpd); + public void startLegacyVpn(VpnProfile profile) { + throwIfLockdownEnabled(); + final LinkProperties egress = getActiveLinkProperties(); + if (egress == null) { + throw new IllegalStateException("Missing active network connection"); + } + mVpn.startLegacyVpn(profile, mKeyStore, egress); } /** @@ -3024,6 +3215,7 @@ private NetworkStateTracker makeWimaxStateTracker() { */ @Override public LegacyVpnInfo getLegacyVpnInfo() { + throwIfLockdownEnabled(); return mVpn.getLegacyVpnInfo(); } @@ -3038,10 +3230,13 @@ private NetworkStateTracker makeWimaxStateTracker() { * be done whenever a better abstraction is developed. */ public class VpnCallback { - private VpnCallback() { } + public void onStateChanged(NetworkInfo info) { + mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget(); + } + public void override(List<String> dnsServers, List<String> searchDomains) { if (dnsServers == null) { restore(); @@ -3108,4 +3303,58 @@ private NetworkStateTracker makeWimaxStateTracker() { } } } + + @Override + public boolean updateLockdownVpn() { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + // Tear down existing lockdown if profile was removed + mLockdownEnabled = LockdownVpnTracker.isEnabled(); + if (mLockdownEnabled) { + if (mKeyStore.state() != KeyStore.State.UNLOCKED) { + Slog.w(TAG, "KeyStore locked; unable to create LockdownTracker"); + return false; + } + + final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN)); + final VpnProfile profile = VpnProfile.decode( + profileName, mKeyStore.get(Credentials.VPN + profileName)); + setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile)); + } else { + setLockdownTracker(null); + } + + return true; + } + + /** + * Internally set new {@link LockdownVpnTracker}, shutting down any existing + * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown. + */ + private void setLockdownTracker(LockdownVpnTracker tracker) { + // Shutdown any existing tracker + final LockdownVpnTracker existing = mLockdownTracker; + mLockdownTracker = null; + if (existing != null) { + existing.shutdown(); + } + + try { + if (tracker != null) { + mNetd.setFirewallEnabled(true); + mLockdownTracker = tracker; + mLockdownTracker.init(); + } else { + mNetd.setFirewallEnabled(false); + } + } catch (RemoteException e) { + // ignored; NMS lives inside system_server + } + } + + private void throwIfLockdownEnabled() { + if (mLockdownEnabled) { + throw new IllegalStateException("Unavailable in lockdown mode"); + } + } } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index ea19d6e..f966a33 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -1626,7 +1626,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mLastMaximumTimeToLock = timeMs; try { - getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); + getIPowerManager().setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs); } catch (RemoteException e) { Slog.w(TAG, "Failure talking with power manager", e); } @@ -1667,8 +1667,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = Binder.clearCallingIdentity(); try { // Power off the display - mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(), - WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN); + getIPowerManager().goToSleep(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN); // Ensure the device is locked getWindowManager().lockNow(); } catch (RemoteException e) { diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index 0ed5189..9231674 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -16,6 +16,9 @@ package com.android.server; +import java.io.FileDescriptor; +import java.io.PrintWriter; + import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -24,6 +27,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; import android.os.Binder; import android.os.Environment; import android.os.FileObserver; @@ -36,8 +40,10 @@ import android.os.StatFs; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; +import android.text.format.Formatter; import android.util.EventLog; import android.util.Slog; +import android.util.TimeUtils; /** * This class implements a service to monitor the amount of disk @@ -71,6 +77,7 @@ public class DeviceStorageMonitorService extends Binder { private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000; private static final int DEFAULT_FULL_THRESHOLD_BYTES = 1024*1024; // 1MB private long mFreeMem; // on /data + private long mFreeMemAfterLastCacheClear; // on /data private long mLastReportedFreeMem; private long mLastReportedFreeMemTime; private boolean mLowMemFlag=false; @@ -95,7 +102,19 @@ public class DeviceStorageMonitorService extends Binder { private final CacheFileDeletedObserver mCacheFileDeletedObserver; private static final int _TRUE = 1; private static final int _FALSE = 0; + // This is the raw threshold that has been set at which we consider + // storage to be low. private long mMemLowThreshold; + // This is the threshold at which we start trying to flush caches + // to get below the low threshold limit. It is less than the low + // threshold; we will allow storage to get a bit beyond the limit + // before flushing and checking if we are actually low. + private long mMemCacheStartTrimThreshold; + // This is the threshold that we try to get to when deleting cache + // files. This is greater than the low threshold so that we will flush + // more files than absolutely needed, to reduce the frequency that + // flushing takes place. + private long mMemCacheTrimToThreshold; private int mMemFullThreshold; /** @@ -190,7 +209,7 @@ public class DeviceStorageMonitorService extends Binder { try { if (localLOGV) Slog.i(TAG, "Clearing cache"); IPackageManager.Stub.asInterface(ServiceManager.getService("package")). - freeStorageAndNotify(mMemLowThreshold, mClearCacheObserver); + freeStorageAndNotify(mMemCacheTrimToThreshold, mClearCacheObserver); } catch (RemoteException e) { Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e); mClearingCache = false; @@ -216,24 +235,42 @@ public class DeviceStorageMonitorService extends Binder { //post intent to NotificationManager to display icon if necessary if (mFreeMem < mMemLowThreshold) { - if (!mLowMemFlag) { - if (checkCache) { - // See if clearing cache helps - // Note that clearing cache is asynchronous and so we do a - // memory check again once the cache has been cleared. - mThreadStartTime = System.currentTimeMillis(); - mClearSucceeded = false; - clearCache(); - } else { + if (checkCache) { + // We are allowed to clear cache files at this point to + // try to get down below the limit, because this is not + // the initial call after a cache clear has been attempted. + // In this case we will try a cache clear if our free + // space has gone below the cache clear limit. + if (mFreeMem < mMemCacheStartTrimThreshold) { + // We only clear the cache if the free storage has changed + // a significant amount since the last time. + if ((mFreeMemAfterLastCacheClear-mFreeMem) + >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) { + // See if clearing cache helps + // Note that clearing cache is asynchronous and so we do a + // memory check again once the cache has been cleared. + mThreadStartTime = System.currentTimeMillis(); + mClearSucceeded = false; + clearCache(); + } + } + } else { + // This is a call from after clearing the cache. Note + // the amount of free storage at this point. + mFreeMemAfterLastCacheClear = mFreeMem; + if (!mLowMemFlag) { + // We tried to clear the cache, but that didn't get us + // below the low storage limit. Tell the user. Slog.i(TAG, "Running low on memory. Sending notification"); sendNotification(); mLowMemFlag = true; + } else { + if (localLOGV) Slog.v(TAG, "Running low on memory " + + "notification already sent. do nothing"); } - } else { - if (localLOGV) Slog.v(TAG, "Running low on memory " + - "notification already sent. do nothing"); } } else { + mFreeMemAfterLastCacheClear = mFreeMem; if (mLowMemFlag) { Slog.i(TAG, "Memory available. Cancelling notification"); cancelNotification(); @@ -276,7 +313,7 @@ public class DeviceStorageMonitorService extends Binder { Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value); - value *= mTotalMemory; + value = (value*mTotalMemory)/100; long maxValue = Settings.Secure.getInt( mContentResolver, Settings.Secure.SYS_STORAGE_THRESHOLD_MAX_BYTES, @@ -312,8 +349,8 @@ public class DeviceStorageMonitorService extends Binder { mSystemFileStats = new StatFs(SYSTEM_PATH); mCacheFileStats = new StatFs(CACHE_PATH); //initialize total storage on device - mTotalMemory = ((long)mDataFileStats.getBlockCount() * - mDataFileStats.getBlockSize())/100L; + mTotalMemory = (long)mDataFileStats.getBlockCount() * + mDataFileStats.getBlockSize(); mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW); mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK); @@ -325,6 +362,10 @@ public class DeviceStorageMonitorService extends Binder { // cache storage thresholds mMemLowThreshold = getMemThreshold(); mMemFullThreshold = getMemFullThreshold(); + mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4; + mMemCacheTrimToThreshold = mMemLowThreshold + + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2); + mFreeMemAfterLastCacheClear = mTotalMemory; checkMemory(true); mCacheFileDeletedObserver = new CacheFileDeletedObserver(); @@ -435,4 +476,40 @@ public class DeviceStorageMonitorService extends Binder { EventLogTags.writeCacheFileDeleted(path); } } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + + pw.println("Permission Denial: can't dump " + SERVICE + " from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + pw.println("Current DeviceStorageMonitor state:"); + pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(mContext, mFreeMem)); + pw.print(" mTotalMemory="); + pw.println(Formatter.formatFileSize(mContext, mTotalMemory)); + pw.print(" mFreeMemAfterLastCacheClear="); + pw.println(Formatter.formatFileSize(mContext, mFreeMemAfterLastCacheClear)); + pw.print(" mLastReportedFreeMem="); + pw.print(Formatter.formatFileSize(mContext, mLastReportedFreeMem)); + pw.print(" mLastReportedFreeMemTime="); + TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw); + pw.println(); + pw.print(" mLowMemFlag="); pw.print(mLowMemFlag); + pw.print(" mMemFullFlag="); pw.println(mMemFullFlag); + pw.print(" mClearSucceeded="); pw.print(mClearSucceeded); + pw.print(" mClearingCache="); pw.println(mClearingCache); + pw.print(" mMemLowThreshold="); + pw.print(Formatter.formatFileSize(mContext, mMemLowThreshold)); + pw.print(" mMemFullThreshold="); + pw.println(Formatter.formatFileSize(mContext, mMemFullThreshold)); + pw.print(" mMemCacheStartTrimThreshold="); + pw.print(Formatter.formatFileSize(mContext, mMemCacheStartTrimThreshold)); + pw.print(" mMemCacheTrimToThreshold="); + pw.println(Formatter.formatFileSize(mContext, mMemCacheTrimToThreshold)); + } } diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 64789d3..f1ff27f 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -16,8 +16,9 @@ package com.android.server; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; +import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK; +import static android.provider.Settings.Secure.SCREENSAVER_ENABLED; + import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -26,11 +27,15 @@ import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.PowerManager; import android.os.SystemClock; import android.os.UEventObserver; import android.provider.Settings; -import android.server.BluetoothService; +import android.service.dreams.IDreamManager; import android.util.Log; import android.util.Slog; @@ -40,14 +45,19 @@ import java.io.FileReader; /** * <p>DockObserver monitors for a docking station. */ -class DockObserver extends UEventObserver { +final class DockObserver extends UEventObserver { private static final String TAG = DockObserver.class.getSimpleName(); private static final boolean LOG = false; private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; - private static final int MSG_DOCK_STATE = 0; + private static final int DEFAULT_SCREENSAVER_ENABLED = 1; + private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1; + + private static final int MSG_DOCK_STATE_CHANGED = 0; + + private final Object mLock = new Object(); private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; @@ -56,11 +66,8 @@ class DockObserver extends UEventObserver { private final Context mContext; - private PowerManagerService mPowerManager; - - public DockObserver(Context context, PowerManagerService pm) { + public DockObserver(Context context) { mContext = context; - mPowerManager = pm; init(); // set initial status startObserving(DOCK_UEVENT_MATCH); @@ -72,7 +79,7 @@ class DockObserver extends UEventObserver { Slog.v(TAG, "Dock UEVENT: " + event.toString()); } - synchronized (this) { + synchronized (mLock) { try { int newState = Integer.parseInt(event.get("SWITCH_STATE")); if (newState != mDockState) { @@ -86,10 +93,11 @@ class DockObserver extends UEventObserver { && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { - mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), - false, true); + PowerManager pm = + (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + pm.wakeUp(SystemClock.uptimeMillis()); } - update(); + updateLocked(); } } } catch (NumberFormatException e) { @@ -98,102 +106,147 @@ class DockObserver extends UEventObserver { } } - private final void init() { - char[] buffer = new char[1024]; - - try { - FileReader file = new FileReader(DOCK_STATE_PATH); - int len = file.read(buffer, 0, 1024); - file.close(); - mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); - } catch (FileNotFoundException e) { - Slog.w(TAG, "This kernel does not have dock station support"); - } catch (Exception e) { - Slog.e(TAG, "" , e); + private void init() { + synchronized (mLock) { + try { + char[] buffer = new char[1024]; + FileReader file = new FileReader(DOCK_STATE_PATH); + try { + int len = file.read(buffer, 0, 1024); + mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); + mPreviousDockState = mDockState; + } finally { + file.close(); + } + } catch (FileNotFoundException e) { + Slog.w(TAG, "This kernel does not have dock station support"); + } catch (Exception e) { + Slog.e(TAG, "" , e); + } } } void systemReady() { - synchronized (this) { + synchronized (mLock) { // don't bother broadcasting undocked here if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { - update(); + updateLocked(); } mSystemReady = true; } } - private final void update() { - mHandler.sendEmptyMessage(MSG_DOCK_STATE); + private void updateLocked() { + mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED); } - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_DOCK_STATE: - synchronized (this) { - Slog.i(TAG, "Dock state changed: " + mDockState); + private void handleDockStateChange() { + synchronized (mLock) { + Slog.i(TAG, "Dock state changed: " + mDockState); - final ContentResolver cr = mContext.getContentResolver(); + final ContentResolver cr = mContext.getContentResolver(); - if (Settings.Secure.getInt(cr, - Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { - Slog.i(TAG, "Device not provisioned, skipping dock broadcast"); - return; - } - // Pack up the values and broadcast them to everyone - Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); - - // Check if this is Bluetooth Dock - String address = BluetoothService.readDockBluetoothAddress(); - if (address != null) - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, - BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); - - // User feedback to confirm dock connection. Particularly - // useful for flaky contact pins... - if (Settings.System.getInt(cr, - Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) - { - String whichSound = null; - if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { - if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) || - (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || - (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { - whichSound = Settings.System.DESK_UNDOCK_SOUND; - } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { - whichSound = Settings.System.CAR_UNDOCK_SOUND; - } - } else { - if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) || - (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || - (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { - whichSound = Settings.System.DESK_DOCK_SOUND; - } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { - whichSound = Settings.System.CAR_DOCK_SOUND; - } - } + if (Settings.Secure.getInt(cr, + Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { + Slog.i(TAG, "Device not provisioned, skipping dock broadcast"); + return; + } + + // Pack up the values and broadcast them to everyone + Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); + + // Check if this is Bluetooth Dock + // TODO(BT): Get Dock address. + // String address = null; + // if (address != null) { + // intent.putExtra(BluetoothDevice.EXTRA_DEVICE, + // BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); + // } + + // User feedback to confirm dock connection. Particularly + // useful for flaky contact pins... + if (Settings.System.getInt(cr, + Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) { + String whichSound = null; + if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { + if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) || + (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || + (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { + whichSound = Settings.System.DESK_UNDOCK_SOUND; + } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { + whichSound = Settings.System.CAR_UNDOCK_SOUND; + } + } else { + if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) || + (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || + (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { + whichSound = Settings.System.DESK_DOCK_SOUND; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { + whichSound = Settings.System.CAR_DOCK_SOUND; + } + } - if (whichSound != null) { - final String soundPath = Settings.System.getString(cr, whichSound); - if (soundPath != null) { - final Uri soundUri = Uri.parse("file://" + soundPath); - if (soundUri != null) { - final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); - if (sfx != null) { - sfx.setStreamType(AudioManager.STREAM_SYSTEM); - sfx.play(); - } - } - } + if (whichSound != null) { + final String soundPath = Settings.System.getString(cr, whichSound); + if (soundPath != null) { + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); } } + } + } + } - mContext.sendStickyBroadcast(intent); + IDreamManager mgr = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); + if (mgr != null) { + // dreams feature enabled + boolean undocked = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED; + if (undocked) { + try { + if (mgr.isDreaming()) { + mgr.awaken(); + } + } catch (RemoteException e) { + Slog.w(TAG, "Unable to awaken!", e); } + } else { + if (isScreenSaverEnabled(mContext) && isScreenSaverActivatedOnDock(mContext)) { + try { + mgr.dream(); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to dream!", e); + } + } + } + } else { + // dreams feature not enabled, send legacy intent + mContext.sendStickyBroadcast(intent); + } + } + } + + private static boolean isScreenSaverEnabled(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED) != 0; + } + + private static boolean isScreenSaverActivatedOnDock(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0; + } + + private final Handler mHandler = new Handler(true /*async*/) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_DOCK_STATE_CHANGED: + handleDockStateChange(); break; } } diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags index 41f7335..dd50beb 100644 --- a/services/java/com/android/server/EventLogTags.logtags +++ b/services/java/com/android/server/EventLogTags.logtags @@ -114,6 +114,8 @@ option java_package com.android.server # Package Manager ready: 3100 boot_progress_pms_ready (time|2|3) # + check activity_launch_time for Home app +# Value of "unknown sources" setting at app install time +3110 unknown_sources_enabled (value|1) # --------------------------- diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index fdb278d..747cf0b 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -16,8 +16,8 @@ package com.android.server; import com.android.internal.content.PackageMonitor; -import com.android.internal.os.AtomicFile; import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; import com.android.internal.util.FastXmlSerializer; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethod; @@ -74,6 +74,7 @@ import android.provider.Settings.Secure; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.text.style.SuggestionSpan; +import android.util.AtomicFile; import android.util.EventLog; import android.util.LruCache; import android.util.Pair; @@ -2024,7 +2025,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean handleMessage(Message msg) { - HandlerCaller.SomeArgs args; + SomeArgs args; switch (msg.what) { case MSG_SHOW_IM_PICKER: showInputMethodMenu(); @@ -2035,8 +2036,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; case MSG_SHOW_IM_SUBTYPE_ENABLER: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; showInputMethodAndSubtypeEnabler((String)args.arg1); + args.recycle(); return true; case MSG_SHOW_IM_CONFIG: @@ -2053,48 +2055,53 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; case MSG_BIND_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_SHOW_SOFT_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_HIDE_SOFT_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_ATTACH_TOKEN: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2); ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_CREATE_SESSION: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethod)args.arg1).createSession( (IInputMethodCallback)args.arg2); } catch (RemoteException e) { } + args.recycle(); return true; // --------------------------------------------------------- case MSG_START_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); @@ -2102,9 +2109,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub (EditorInfo)args.arg3); } catch (RemoteException e) { } + args.recycle(); return true; case MSG_RESTART_INPUT: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { SessionState session = (SessionState)args.arg1; setEnabledSessionInMainThread(session); @@ -2112,6 +2120,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub (EditorInfo)args.arg3); } catch (RemoteException e) { } + args.recycle(); return true; // --------------------------------------------------------- @@ -2124,13 +2133,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; case MSG_BIND_METHOD: - args = (HandlerCaller.SomeArgs)msg.obj; + args = (SomeArgs)msg.obj; try { ((IInputMethodClient)args.arg1).onBindMethod( (InputBindResult)args.arg2); } catch (RemoteException e) { Slog.w(TAG, "Client died receiving input method " + args.arg2); } + args.recycle(); return true; case MSG_SET_ACTIVE: try { diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index f7e841e..d4769e8 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -34,6 +34,7 @@ import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.LogPrinter; import android.util.Printer; +import android.util.StringBuilderPrinter; import android.content.Intent; import android.content.IntentFilter; @@ -65,11 +66,17 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { register_intent_filter(f, f.actionsIterator(), mTypedActionToFilter, " TypedAction: "); } + + mOldResolver.addFilter(f); + verifyDataStructures(f); } public void removeFilter(F f) { removeFilterInternal(f); mFilters.remove(f); + + mOldResolver.removeFilter(f); + verifyDataStructures(f); } void removeFilterInternal(F f) { @@ -93,18 +100,18 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { } boolean dumpMap(PrintWriter out, String titlePrefix, String title, - String prefix, Map<String, ArrayList<F>> map, String packageName, + String prefix, Map<String, F[]> map, String packageName, boolean printFilter) { String eprefix = prefix + " "; String fprefix = prefix + " "; boolean printedSomething = false; Printer printer = null; - for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) { - ArrayList<F> a = e.getValue(); - final int N = a.size(); + for (Map.Entry<String, F[]> e : map.entrySet()) { + F[] a = e.getValue(); + final int N = a.length; boolean printedHeader = false; - for (int i=0; i<N; i++) { - F filter = a.get(i); + F filter; + for (int i=0; i<N && (filter=a[i]) != null; i++) { if (packageName != null && !packageName.equals(packageForFilter(filter))) { continue; } @@ -201,7 +208,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { } public List<R> queryIntentFromList(Intent intent, String resolvedType, - boolean defaultOnly, ArrayList<ArrayList<F>> listCut, int userId) { + boolean defaultOnly, ArrayList<F[]> listCut, int userId) { ArrayList<R> resultList = new ArrayList<R>(); final boolean debug = localLOGV || @@ -231,10 +238,10 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { TAG, "Resolving type " + resolvedType + " scheme " + scheme + " of intent " + intent); - ArrayList<F> firstTypeCut = null; - ArrayList<F> secondTypeCut = null; - ArrayList<F> thirdTypeCut = null; - ArrayList<F> schemeCut = null; + F[] firstTypeCut = null; + F[] secondTypeCut = null; + F[] thirdTypeCut = null; + F[] schemeCut = null; // If the intent includes a MIME type, then we want to collect all of // the filters that match that MIME type. @@ -307,6 +314,14 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { } sortResults(finalList); + List<R> oldList = mOldResolver.queryIntent(intent, resolvedType, defaultOnly, userId); + if (oldList.size() != finalList.size()) { + ValidationFailure here = new ValidationFailure(); + here.fillInStackTrace(); + Log.wtf(TAG, "Query result " + intent + " size is " + finalList.size() + + "; old implementation is " + oldList.size(), here); + } + if (debug) { Slog.v(TAG, "Final result list:"); for (R r : finalList) { @@ -340,7 +355,9 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { * they are to be delivered to. */ protected abstract String packageForFilter(F filter); - + + protected abstract F[] newArray(int size); + @SuppressWarnings("unchecked") protected R newResult(F filter, int match, int userId) { return (R)filter; @@ -355,6 +372,29 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { out.print(prefix); out.println(filter); } + private final void addFilter(HashMap<String, F[]> map, String name, F filter) { + F[] array = map.get(name); + if (array == null) { + array = newArray(2); + map.put(name, array); + array[0] = filter; + } else { + final int N = array.length; + int i = N; + while (i > 0 && array[i-1] == null) { + i--; + } + if (i < N) { + array[i] = filter; + } else { + F[] newa = newArray((N*3)/2); + System.arraycopy(array, 0, newa, 0, N); + newa[N] = filter; + map.put(name, newa); + } + } + } + private final int register_mime_types(F filter, String prefix) { final Iterator<String> i = filter.typesIterator(); if (i == null) { @@ -374,30 +414,12 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { name = name + "/*"; } - ArrayList<F> array = mTypeToFilter.get(name); - if (array == null) { - //Slog.v(TAG, "Creating new array for " + name); - array = new ArrayList<F>(); - mTypeToFilter.put(name, array); - } - array.add(filter); + addFilter(mTypeToFilter, name, filter); if (slashpos > 0) { - array = mBaseTypeToFilter.get(baseName); - if (array == null) { - //Slog.v(TAG, "Creating new array for " + name); - array = new ArrayList<F>(); - mBaseTypeToFilter.put(baseName, array); - } - array.add(filter); + addFilter(mBaseTypeToFilter, baseName, filter); } else { - array = mWildTypeToFilter.get(baseName); - if (array == null) { - //Slog.v(TAG, "Creating new array for " + name); - array = new ArrayList<F>(); - mWildTypeToFilter.put(baseName, array); - } - array.add(filter); + addFilter(mWildTypeToFilter, baseName, filter); } } @@ -423,25 +445,19 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { name = name + "/*"; } - if (!remove_all_objects(mTypeToFilter.get(name), filter)) { - mTypeToFilter.remove(name); - } + remove_all_objects(mTypeToFilter, name, filter); if (slashpos > 0) { - if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) { - mBaseTypeToFilter.remove(baseName); - } + remove_all_objects(mBaseTypeToFilter, baseName, filter); } else { - if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) { - mWildTypeToFilter.remove(baseName); - } + remove_all_objects(mWildTypeToFilter, baseName, filter); } } return num; } private final int register_intent_filter(F filter, Iterator<String> i, - HashMap<String, ArrayList<F>> dest, String prefix) { + HashMap<String, F[]> dest, String prefix) { if (i == null) { return 0; } @@ -451,19 +467,13 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); - ArrayList<F> array = dest.get(name); - if (array == null) { - //Slog.v(TAG, "Creating new array for " + name); - array = new ArrayList<F>(); - dest.put(name, array); - } - array.add(filter); + addFilter(dest, name, filter); } return num; } private final int unregister_intent_filter(F filter, Iterator<String> i, - HashMap<String, ArrayList<F>> dest, String prefix) { + HashMap<String, F[]> dest, String prefix) { if (i == null) { return 0; } @@ -473,26 +483,37 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); - if (!remove_all_objects(dest.get(name), filter)) { - dest.remove(name); - } + remove_all_objects(dest, name, filter); } return num; } - private final boolean remove_all_objects(List<F> list, Object object) { - if (list != null) { - int N = list.size(); - for (int idx=0; idx<N; idx++) { - if (list.get(idx) == object) { - list.remove(idx); - idx--; - N--; + private final void remove_all_objects(HashMap<String, F[]> map, String name, + Object object) { + F[] array = map.get(name); + if (array != null) { + int LAST = array.length-1; + while (LAST >= 0 && array[LAST] == null) { + LAST--; + } + for (int idx=LAST; idx>=0; idx--) { + if (array[idx] == object) { + final int remain = LAST - idx; + if (remain > 0) { + System.arraycopy(array, idx+1, array, idx, remain); + } + array[LAST] = null; + LAST--; } } - return N > 0; + if (LAST < 0) { + map.remove(name); + } else if (LAST < (array.length/2)) { + F[] newa = newArray(LAST+2); + System.arraycopy(array, 0, newa, 0, LAST+1); + map.put(name, newa); + } } - return false; } private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) { @@ -505,18 +526,18 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly, - String resolvedType, String scheme, List<F> src, List<R> dest, int userId) { + String resolvedType, String scheme, F[] src, List<R> dest, int userId) { final String action = intent.getAction(); final Uri data = intent.getData(); final String packageName = intent.getPackage(); final boolean excludingStopped = intent.isExcludingStopped(); - final int N = src != null ? src.size() : 0; + final int N = src != null ? src.length : 0; boolean hasNonDefaults = false; int i; - for (i=0; i<N; i++) { - F filter = src.get(i); + F filter; + for (i=0; i<N && (filter=src[i]) != null; i++) { int match; if (debug) Slog.v(TAG, "Matching against filter " + filter); @@ -585,6 +606,120 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { } }; + static class ValidationFailure extends RuntimeException { + } + + private void verifyDataStructures(IntentFilter src) { + compareMaps(src, "mTypeToFilter", mTypeToFilter, mOldResolver.mTypeToFilter); + compareMaps(src, "mBaseTypeToFilter", mBaseTypeToFilter, mOldResolver.mBaseTypeToFilter); + compareMaps(src, "mWildTypeToFilter", mWildTypeToFilter, mOldResolver.mWildTypeToFilter); + compareMaps(src, "mSchemeToFilter", mSchemeToFilter, mOldResolver.mSchemeToFilter); + compareMaps(src, "mActionToFilter", mActionToFilter, mOldResolver.mActionToFilter); + compareMaps(src, "mTypedActionToFilter", mTypedActionToFilter, mOldResolver.mTypedActionToFilter); + } + + private void compareMaps(IntentFilter src, String name, HashMap<String, F[]> cur, + HashMap<String, ArrayList<F>> old) { + if (cur.size() != old.size()) { + StringBuilder missing = new StringBuilder(128); + for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) { + final F[] curArray = cur.get(e.getKey()); + if (curArray == null) { + if (missing.length() > 0) { + missing.append(' '); + } + missing.append(e.getKey()); + } + } + StringBuilder extra = new StringBuilder(128); + for (Map.Entry<String, F[]> e : cur.entrySet()) { + if (old.get(e.getKey()) == null) { + if (extra.length() > 0) { + extra.append(' '); + } + extra.append(e.getKey()); + } + } + StringBuilder srcStr = new StringBuilder(1024); + StringBuilderPrinter printer = new StringBuilderPrinter(srcStr); + src.dump(printer, ""); + ValidationFailure here = new ValidationFailure(); + here.fillInStackTrace(); + Log.wtf(TAG, "New map " + name + " size is " + cur.size() + + "; old implementation is " + old.size() + + "; missing: " + missing.toString() + + "; extra: " + extra.toString() + + "; src: " + srcStr.toString(), here); + return; + } + for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) { + final F[] curArray = cur.get(e.getKey()); + int curLen = curArray != null ? curArray.length : 0; + if (curLen == 0) { + ValidationFailure here = new ValidationFailure(); + here.fillInStackTrace(); + Log.wtf(TAG, "New map " + name + " doesn't contain expected key " + + e.getKey() + " (array=" + curArray + ")"); + return; + } + while (curLen > 0 && curArray[curLen-1] == null) { + curLen--; + } + final ArrayList<F> oldArray = e.getValue(); + final int oldLen = oldArray.size(); + if (curLen != oldLen) { + ValidationFailure here = new ValidationFailure(); + here.fillInStackTrace(); + Log.wtf(TAG, "New map " + name + " entry " + e.getKey() + " size is " + + curLen + "; old implementation is " + oldLen, here); + return; + } + for (int i=0; i<oldLen; i++) { + F f = oldArray.get(i); + boolean found = false; + for (int j=0; j<curLen; j++) { + if (curArray[j] == f) { + found = true; + break; + } + } + if (!found) { + ValidationFailure here = new ValidationFailure(); + here.fillInStackTrace(); + Log.wtf(TAG, "New map " + name + " entry + " + e.getKey() + + " doesn't contain expected filter " + f, here); + } + } + for (int i=0; i<curLen; i++) { + if (curArray[i] == null) { + ValidationFailure here = new ValidationFailure(); + here.fillInStackTrace(); + Log.wtf(TAG, "New map " + name + " entry + " + e.getKey() + + " has unexpected null at " + i + "; array: " + curArray, here); + break; + } + } + } + } + + private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() { + @Override protected String packageForFilter(F filter) { + return IntentResolver.this.packageForFilter(filter); + } + @Override protected boolean allowFilterResult(F filter, List<R> dest) { + return IntentResolver.this.allowFilterResult(filter, dest); + } + @Override protected boolean isFilterStopped(F filter, int userId) { + return IntentResolver.this.isFilterStopped(filter, userId); + } + @Override protected R newResult(F filter, int match, int userId) { + return IntentResolver.this.newResult(filter, match, userId); + } + @Override protected void sortResults(List<R> results) { + IntentResolver.this.sortResults(results); + } + }; + /** * All filters that have been registered. */ @@ -594,16 +729,14 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { * All of the MIME types that have been registered, such as "image/jpeg", * "image/*", or "{@literal *}/*". */ - private final HashMap<String, ArrayList<F>> mTypeToFilter - = new HashMap<String, ArrayList<F>>(); + private final HashMap<String, F[]> mTypeToFilter = new HashMap<String, F[]>(); /** * The base names of all of all fully qualified MIME types that have been * registered, such as "image" or "*". Wild card MIME types such as * "image/*" will not be here. */ - private final HashMap<String, ArrayList<F>> mBaseTypeToFilter - = new HashMap<String, ArrayList<F>>(); + private final HashMap<String, F[]> mBaseTypeToFilter = new HashMap<String, F[]>(); /** * The base names of all of the MIME types with a sub-type wildcard that @@ -612,26 +745,21 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { * included here. This also includes the "*" for the "{@literal *}/*" * MIME type. */ - private final HashMap<String, ArrayList<F>> mWildTypeToFilter - = new HashMap<String, ArrayList<F>>(); + private final HashMap<String, F[]> mWildTypeToFilter = new HashMap<String, F[]>(); /** * All of the URI schemes (such as http) that have been registered. */ - private final HashMap<String, ArrayList<F>> mSchemeToFilter - = new HashMap<String, ArrayList<F>>(); + private final HashMap<String, F[]> mSchemeToFilter = new HashMap<String, F[]>(); /** * All of the actions that have been registered, but only those that did * not specify data. */ - private final HashMap<String, ArrayList<F>> mActionToFilter - = new HashMap<String, ArrayList<F>>(); + private final HashMap<String, F[]> mActionToFilter = new HashMap<String, F[]>(); /** * All of the actions that have been registered and specified a MIME type. */ - private final HashMap<String, ArrayList<F>> mTypedActionToFilter - = new HashMap<String, ArrayList<F>>(); + private final HashMap<String, F[]> mTypedActionToFilter = new HashMap<String, F[]>(); } - diff --git a/services/java/com/android/server/IntentResolverOld.java b/services/java/com/android/server/IntentResolverOld.java new file mode 100644 index 0000000..4dd77ce --- /dev/null +++ b/services/java/com/android/server/IntentResolverOld.java @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2006 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; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import android.net.Uri; +import android.util.FastImmutableArraySet; +import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.Slog; +import android.util.LogPrinter; +import android.util.Printer; + +import android.content.Intent; +import android.content.IntentFilter; + +/** + * Temporary for verification of new implementation. + * {@hide} + */ +public abstract class IntentResolverOld<F extends IntentFilter, R extends Object> { + final private static String TAG = "IntentResolver"; + final private static boolean DEBUG = false; + final private static boolean localLOGV = DEBUG || false; + + public void addFilter(F f) { + if (localLOGV) { + Slog.v(TAG, "Adding filter: " + f); + f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + Slog.v(TAG, " Building Lookup Maps:"); + } + + mFilters.add(f); + int numS = register_intent_filter(f, f.schemesIterator(), + mSchemeToFilter, " Scheme: "); + int numT = register_mime_types(f, " Type: "); + if (numS == 0 && numT == 0) { + register_intent_filter(f, f.actionsIterator(), + mActionToFilter, " Action: "); + } + if (numT != 0) { + register_intent_filter(f, f.actionsIterator(), + mTypedActionToFilter, " TypedAction: "); + } + } + + public void removeFilter(F f) { + removeFilterInternal(f); + mFilters.remove(f); + } + + void removeFilterInternal(F f) { + if (localLOGV) { + Slog.v(TAG, "Removing filter: " + f); + f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); + Slog.v(TAG, " Cleaning Lookup Maps:"); + } + + int numS = unregister_intent_filter(f, f.schemesIterator(), + mSchemeToFilter, " Scheme: "); + int numT = unregister_mime_types(f, " Type: "); + if (numS == 0 && numT == 0) { + unregister_intent_filter(f, f.actionsIterator(), + mActionToFilter, " Action: "); + } + if (numT != 0) { + unregister_intent_filter(f, f.actionsIterator(), + mTypedActionToFilter, " TypedAction: "); + } + } + + boolean dumpMap(PrintWriter out, String titlePrefix, String title, + String prefix, Map<String, ArrayList<F>> map, String packageName, + boolean printFilter) { + String eprefix = prefix + " "; + String fprefix = prefix + " "; + boolean printedSomething = false; + Printer printer = null; + for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) { + ArrayList<F> a = e.getValue(); + final int N = a.size(); + boolean printedHeader = false; + for (int i=0; i<N; i++) { + F filter = a.get(i); + if (packageName != null && !packageName.equals(packageForFilter(filter))) { + continue; + } + if (title != null) { + out.print(titlePrefix); out.println(title); + title = null; + } + if (!printedHeader) { + out.print(eprefix); out.print(e.getKey()); out.println(":"); + printedHeader = true; + } + printedSomething = true; + dumpFilter(out, fprefix, filter); + if (printFilter) { + if (printer == null) { + printer = new PrintWriterPrinter(out); + } + filter.dump(printer, fprefix + " "); + } + } + } + return printedSomething; + } + + public boolean dump(PrintWriter out, String title, String prefix, String packageName, + boolean printFilter) { + String innerPrefix = prefix + " "; + String sepPrefix = "\n" + prefix; + String curPrefix = title + "\n" + prefix; + if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix, + mTypeToFilter, packageName, printFilter)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix, + mBaseTypeToFilter, packageName, printFilter)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix, + mWildTypeToFilter, packageName, printFilter)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Schemes:", innerPrefix, + mSchemeToFilter, packageName, printFilter)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix, + mActionToFilter, packageName, printFilter)) { + curPrefix = sepPrefix; + } + if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix, + mTypedActionToFilter, packageName, printFilter)) { + curPrefix = sepPrefix; + } + return curPrefix == sepPrefix; + } + + private class IteratorWrapper implements Iterator<F> { + private final Iterator<F> mI; + private F mCur; + + IteratorWrapper(Iterator<F> it) { + mI = it; + } + + public boolean hasNext() { + return mI.hasNext(); + } + + public F next() { + return (mCur = mI.next()); + } + + public void remove() { + if (mCur != null) { + removeFilterInternal(mCur); + } + mI.remove(); + } + + } + + /** + * Returns an iterator allowing filters to be removed. + */ + public Iterator<F> filterIterator() { + return new IteratorWrapper(mFilters.iterator()); + } + + /** + * Returns a read-only set of the filters. + */ + public Set<F> filterSet() { + return Collections.unmodifiableSet(mFilters); + } + + public List<R> queryIntentFromList(Intent intent, String resolvedType, + boolean defaultOnly, ArrayList<ArrayList<F>> listCut, int userId) { + ArrayList<R> resultList = new ArrayList<R>(); + + final boolean debug = localLOGV || + ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + + FastImmutableArraySet<String> categories = getFastIntentCategories(intent); + final String scheme = intent.getScheme(); + int N = listCut.size(); + for (int i = 0; i < N; ++i) { + buildResolveList(intent, categories, debug, defaultOnly, + resolvedType, scheme, listCut.get(i), resultList, userId); + } + sortResults(resultList); + return resultList; + } + + public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, + int userId) { + String scheme = intent.getScheme(); + + ArrayList<R> finalList = new ArrayList<R>(); + + final boolean debug = localLOGV || + ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + + if (debug) Slog.v( + TAG, "Resolving type " + resolvedType + " scheme " + scheme + + " of intent " + intent); + + ArrayList<F> firstTypeCut = null; + ArrayList<F> secondTypeCut = null; + ArrayList<F> thirdTypeCut = null; + ArrayList<F> schemeCut = null; + + // If the intent includes a MIME type, then we want to collect all of + // the filters that match that MIME type. + if (resolvedType != null) { + int slashpos = resolvedType.indexOf('/'); + if (slashpos > 0) { + final String baseType = resolvedType.substring(0, slashpos); + if (!baseType.equals("*")) { + if (resolvedType.length() != slashpos+2 + || resolvedType.charAt(slashpos+1) != '*') { + // Not a wild card, so we can just look for all filters that + // completely match or wildcards whose base type matches. + firstTypeCut = mTypeToFilter.get(resolvedType); + if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut); + secondTypeCut = mWildTypeToFilter.get(baseType); + if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut); + } else { + // We can match anything with our base type. + firstTypeCut = mBaseTypeToFilter.get(baseType); + if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut); + secondTypeCut = mWildTypeToFilter.get(baseType); + if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut); + } + // Any */* types always apply, but we only need to do this + // if the intent type was not already */*. + thirdTypeCut = mWildTypeToFilter.get("*"); + if (debug) Slog.v(TAG, "Third type cut: " + thirdTypeCut); + } else if (intent.getAction() != null) { + // The intent specified any type ({@literal *}/*). This + // can be a whole heck of a lot of things, so as a first + // cut let's use the action instead. + firstTypeCut = mTypedActionToFilter.get(intent.getAction()); + if (debug) Slog.v(TAG, "Typed Action list: " + firstTypeCut); + } + } + } + + // If the intent includes a data URI, then we want to collect all of + // the filters that match its scheme (we will further refine matches + // on the authority and path by directly matching each resulting filter). + if (scheme != null) { + schemeCut = mSchemeToFilter.get(scheme); + if (debug) Slog.v(TAG, "Scheme list: " + schemeCut); + } + + // If the intent does not specify any data -- either a MIME type or + // a URI -- then we will only be looking for matches against empty + // data. + if (resolvedType == null && scheme == null && intent.getAction() != null) { + firstTypeCut = mActionToFilter.get(intent.getAction()); + if (debug) Slog.v(TAG, "Action list: " + firstTypeCut); + } + + FastImmutableArraySet<String> categories = getFastIntentCategories(intent); + if (firstTypeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, + resolvedType, scheme, firstTypeCut, finalList, userId); + } + if (secondTypeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, + resolvedType, scheme, secondTypeCut, finalList, userId); + } + if (thirdTypeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, + resolvedType, scheme, thirdTypeCut, finalList, userId); + } + if (schemeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, + resolvedType, scheme, schemeCut, finalList, userId); + } + sortResults(finalList); + + if (debug) { + Slog.v(TAG, "Final result list:"); + for (R r : finalList) { + Slog.v(TAG, " " + r); + } + } + return finalList; + } + + /** + * Control whether the given filter is allowed to go into the result + * list. Mainly intended to prevent adding multiple filters for the + * same target object. + */ + protected boolean allowFilterResult(F filter, List<R> dest) { + return true; + } + + /** + * Returns whether the object associated with the given filter is + * "stopped," that is whether it should not be included in the result + * if the intent requests to excluded stopped objects. + */ + protected boolean isFilterStopped(F filter, int userId) { + return false; + } + + /** + * Return the package that owns this filter. This must be implemented to + * provide correct filtering of Intents that have specified a package name + * they are to be delivered to. + */ + protected abstract String packageForFilter(F filter); + + @SuppressWarnings("unchecked") + protected R newResult(F filter, int match, int userId) { + return (R)filter; + } + + @SuppressWarnings("unchecked") + protected void sortResults(List<R> results) { + Collections.sort(results, mResolvePrioritySorter); + } + + protected void dumpFilter(PrintWriter out, String prefix, F filter) { + out.print(prefix); out.println(filter); + } + + private final int register_mime_types(F filter, String prefix) { + final Iterator<String> i = filter.typesIterator(); + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + String baseName = name; + final int slashpos = name.indexOf('/'); + if (slashpos > 0) { + baseName = name.substring(0, slashpos).intern(); + } else { + name = name + "/*"; + } + + ArrayList<F> array = mTypeToFilter.get(name); + if (array == null) { + //Slog.v(TAG, "Creating new array for " + name); + array = new ArrayList<F>(); + mTypeToFilter.put(name, array); + } + array.add(filter); + + if (slashpos > 0) { + array = mBaseTypeToFilter.get(baseName); + if (array == null) { + //Slog.v(TAG, "Creating new array for " + name); + array = new ArrayList<F>(); + mBaseTypeToFilter.put(baseName, array); + } + array.add(filter); + } else { + array = mWildTypeToFilter.get(baseName); + if (array == null) { + //Slog.v(TAG, "Creating new array for " + name); + array = new ArrayList<F>(); + mWildTypeToFilter.put(baseName, array); + } + array.add(filter); + } + } + + return num; + } + + private final int unregister_mime_types(F filter, String prefix) { + final Iterator<String> i = filter.typesIterator(); + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + String baseName = name; + final int slashpos = name.indexOf('/'); + if (slashpos > 0) { + baseName = name.substring(0, slashpos).intern(); + } else { + name = name + "/*"; + } + + if (!remove_all_objects(mTypeToFilter.get(name), filter)) { + mTypeToFilter.remove(name); + } + + if (slashpos > 0) { + if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) { + mBaseTypeToFilter.remove(baseName); + } + } else { + if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) { + mWildTypeToFilter.remove(baseName); + } + } + } + return num; + } + + private final int register_intent_filter(F filter, Iterator<String> i, + HashMap<String, ArrayList<F>> dest, String prefix) { + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + ArrayList<F> array = dest.get(name); + if (array == null) { + //Slog.v(TAG, "Creating new array for " + name); + array = new ArrayList<F>(); + dest.put(name, array); + } + array.add(filter); + } + return num; + } + + private final int unregister_intent_filter(F filter, Iterator<String> i, + HashMap<String, ArrayList<F>> dest, String prefix) { + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + if (!remove_all_objects(dest.get(name), filter)) { + dest.remove(name); + } + } + return num; + } + + private final boolean remove_all_objects(List<F> list, Object object) { + if (list != null) { + int N = list.size(); + for (int idx=0; idx<N; idx++) { + if (list.get(idx) == object) { + list.remove(idx); + idx--; + N--; + } + } + return N > 0; + } + return false; + } + + private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) { + final Set<String> categories = intent.getCategories(); + if (categories == null) { + return null; + } + return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()])); + } + + private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, + boolean debug, boolean defaultOnly, + String resolvedType, String scheme, List<F> src, List<R> dest, int userId) { + final String action = intent.getAction(); + final Uri data = intent.getData(); + final String packageName = intent.getPackage(); + + final boolean excludingStopped = intent.isExcludingStopped(); + + final int N = src != null ? src.size() : 0; + boolean hasNonDefaults = false; + int i; + for (i=0; i<N; i++) { + F filter = src.get(i); + int match; + if (debug) Slog.v(TAG, "Matching against filter " + filter); + + if (excludingStopped && isFilterStopped(filter, userId)) { + if (debug) { + Slog.v(TAG, " Filter's target is stopped; skipping"); + } + continue; + } + + // Is delivery being limited to filters owned by a particular package? + if (packageName != null && !packageName.equals(packageForFilter(filter))) { + if (debug) { + Slog.v(TAG, " Filter is not from package " + packageName + "; skipping"); + } + continue; + } + + // Do we already have this one? + if (!allowFilterResult(filter, dest)) { + if (debug) { + Slog.v(TAG, " Filter's target already added"); + } + continue; + } + + match = filter.match(action, resolvedType, scheme, data, categories, TAG); + if (match >= 0) { + if (debug) Slog.v(TAG, " Filter matched! match=0x" + + Integer.toHexString(match)); + if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) { + final R oneResult = newResult(filter, match, userId); + if (oneResult != null) { + dest.add(oneResult); + } + } else { + hasNonDefaults = true; + } + } else { + if (debug) { + String reason; + switch (match) { + case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; + case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; + case IntentFilter.NO_MATCH_DATA: reason = "data"; break; + case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; + default: reason = "unknown reason"; break; + } + Slog.v(TAG, " Filter did not match: " + reason); + } + } + } + + if (dest.size() == 0 && hasNonDefaults) { + Slog.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT"); + } + } + + // Sorts a List of IntentFilter objects into descending priority order. + @SuppressWarnings("rawtypes") + private static final Comparator mResolvePrioritySorter = new Comparator() { + public int compare(Object o1, Object o2) { + final int q1 = ((IntentFilter) o1).getPriority(); + final int q2 = ((IntentFilter) o2).getPriority(); + return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0); + } + }; + + /** + * All filters that have been registered. + */ + final HashSet<F> mFilters = new HashSet<F>(); + + /** + * All of the MIME types that have been registered, such as "image/jpeg", + * "image/*", or "{@literal *}/*". + */ + final HashMap<String, ArrayList<F>> mTypeToFilter + = new HashMap<String, ArrayList<F>>(); + + /** + * The base names of all of all fully qualified MIME types that have been + * registered, such as "image" or "*". Wild card MIME types such as + * "image/*" will not be here. + */ + final HashMap<String, ArrayList<F>> mBaseTypeToFilter + = new HashMap<String, ArrayList<F>>(); + + /** + * The base names of all of the MIME types with a sub-type wildcard that + * have been registered. For example, a filter with "image/*" will be + * included here as "image" but one with "image/jpeg" will not be + * included here. This also includes the "*" for the "{@literal *}/*" + * MIME type. + */ + final HashMap<String, ArrayList<F>> mWildTypeToFilter + = new HashMap<String, ArrayList<F>>(); + + /** + * All of the URI schemes (such as http) that have been registered. + */ + final HashMap<String, ArrayList<F>> mSchemeToFilter + = new HashMap<String, ArrayList<F>>(); + + /** + * All of the actions that have been registered, but only those that did + * not specify data. + */ + final HashMap<String, ArrayList<F>> mActionToFilter + = new HashMap<String, ArrayList<F>>(); + + /** + * All of the actions that have been registered and specified a MIME type. + */ + final HashMap<String, ArrayList<F>> mTypedActionToFilter + = new HashMap<String, ArrayList<F>>(); +} + diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java index 1e95f3e..89bfcac 100644 --- a/services/java/com/android/server/LightsService.java +++ b/services/java/com/android/server/LightsService.java @@ -31,29 +31,29 @@ public class LightsService { private static final String TAG = "LightsService"; private static final boolean DEBUG = false; - static final int LIGHT_ID_BACKLIGHT = 0; - static final int LIGHT_ID_KEYBOARD = 1; - static final int LIGHT_ID_BUTTONS = 2; - static final int LIGHT_ID_BATTERY = 3; - static final int LIGHT_ID_NOTIFICATIONS = 4; - static final int LIGHT_ID_ATTENTION = 5; - static final int LIGHT_ID_BLUETOOTH = 6; - static final int LIGHT_ID_WIFI = 7; - static final int LIGHT_ID_COUNT = 8; - - static final int LIGHT_FLASH_NONE = 0; - static final int LIGHT_FLASH_TIMED = 1; - static final int LIGHT_FLASH_HARDWARE = 2; + public static final int LIGHT_ID_BACKLIGHT = 0; + public static final int LIGHT_ID_KEYBOARD = 1; + public static final int LIGHT_ID_BUTTONS = 2; + public static final int LIGHT_ID_BATTERY = 3; + public static final int LIGHT_ID_NOTIFICATIONS = 4; + public static final int LIGHT_ID_ATTENTION = 5; + public static final int LIGHT_ID_BLUETOOTH = 6; + public static final int LIGHT_ID_WIFI = 7; + public static final int LIGHT_ID_COUNT = 8; + + public static final int LIGHT_FLASH_NONE = 0; + public static final int LIGHT_FLASH_TIMED = 1; + public static final int LIGHT_FLASH_HARDWARE = 2; /** * Light brightness is managed by a user setting. */ - static final int BRIGHTNESS_MODE_USER = 0; + public static final int BRIGHTNESS_MODE_USER = 0; /** * Light brightness is managed by a light sensor. */ - static final int BRIGHTNESS_MODE_SENSOR = 1; + public static final int BRIGHTNESS_MODE_SENSOR = 1; private final Light mLights[] = new Light[LIGHT_ID_COUNT]; diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 8c1581c..3a45720 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -16,27 +16,21 @@ package com.android.server; -import android.app.Activity; import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.ContentQueryMap; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.Signature; import android.content.res.Resources; -import android.database.ContentObserver; import android.database.Cursor; import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; +import android.location.Geofence; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; import android.location.ILocationListener; @@ -45,9 +39,8 @@ import android.location.INetInitiatedListener; import android.location.Location; import android.location.LocationManager; import android.location.LocationProvider; +import android.location.LocationRequest; import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -57,17 +50,21 @@ import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.SystemClock; import android.os.WorkSource; import android.provider.Settings; +import android.provider.Settings.NameValueTable; import android.util.Log; import android.util.Slog; -import android.util.PrintWriterPrinter; import com.android.internal.content.PackageMonitor; -import com.android.internal.location.GpsNetInitiatedHandler; - +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; import com.android.server.location.GeocoderProxy; +import com.android.server.location.GeofenceManager; import com.android.server.location.GpsLocationProvider; +import com.android.server.location.LocationBlacklist; +import com.android.server.location.LocationFudger; import com.android.server.location.LocationProviderInterface; import com.android.server.location.LocationProviderProxy; import com.android.server.location.MockProvider; @@ -76,12 +73,9 @@ import com.android.server.location.PassiveProvider; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Observable; @@ -91,154 +85,260 @@ import java.util.Set; /** * The service class that manages LocationProviders and issues location * updates and alerts. - * - * {@hide} */ -public class LocationManagerService extends ILocationManager.Stub implements Runnable { +public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable { private static final String TAG = "LocationManagerService"; - private static final boolean LOCAL_LOGV = false; + public static final boolean D = false; - // The last time a location was written, by provider name. - private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>(); + private static final String WAKELOCK_KEY = TAG; + private static final String THREAD_NAME = TAG; private static final String ACCESS_FINE_LOCATION = - android.Manifest.permission.ACCESS_FINE_LOCATION; + android.Manifest.permission.ACCESS_FINE_LOCATION; private static final String ACCESS_COARSE_LOCATION = - android.Manifest.permission.ACCESS_COARSE_LOCATION; + android.Manifest.permission.ACCESS_COARSE_LOCATION; private static final String ACCESS_MOCK_LOCATION = - android.Manifest.permission.ACCESS_MOCK_LOCATION; + android.Manifest.permission.ACCESS_MOCK_LOCATION; private static final String ACCESS_LOCATION_EXTRA_COMMANDS = - android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS; + android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS; private static final String INSTALL_LOCATION_PROVIDER = - android.Manifest.permission.INSTALL_LOCATION_PROVIDER; + android.Manifest.permission.INSTALL_LOCATION_PROVIDER; + + private static final String NETWORK_LOCATION_SERVICE_ACTION = + "com.android.location.service.v2.NetworkLocationProvider"; + private static final String FUSED_LOCATION_SERVICE_ACTION = + "com.android.location.service.FusedLocationProvider"; - private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist"; - private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist"; + private static final int MSG_LOCATION_CHANGED = 1; // Location Providers may sometimes deliver location updates // slightly faster that requested - provide grace period so // we don't unnecessarily filter events that are otherwise on // time - private static final int MAX_PROVIDER_SCHEDULING_JITTER = 100; + private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100; - // Set of providers that are explicitly enabled - private final Set<String> mEnabledProviders = new HashSet<String>(); + private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest(); - // Set of providers that are explicitly disabled - private final Set<String> mDisabledProviders = new HashSet<String>(); - - // Locations, status values, and extras for mock providers - private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>(); + private final Context mContext; - private static boolean sProvidersLoaded = false; + // used internally for synchronization + private final Object mLock = new Object(); - private final Context mContext; - private PackageManager mPackageManager; // final after initialize() - private String mNetworkLocationProviderPackageName; // only used on handler thread - private String mGeocodeProviderPackageName; // only used on handler thread + // --- fields below are final after init() --- + private LocationFudger mLocationFudger; + private GeofenceManager mGeofenceManager; + private PowerManager.WakeLock mWakeLock; + private PackageManager mPackageManager; private GeocoderProxy mGeocodeProvider; private IGpsStatusProvider mGpsStatusProvider; private INetInitiatedListener mNetInitiatedListener; private LocationWorkerHandler mLocationHandler; + private PassiveProvider mPassiveProvider; // track passive provider for special cases + private LocationBlacklist mBlacklist; - // Cache the real providers for use in addTestProvider() and removeTestProvider() - LocationProviderProxy mNetworkLocationProvider; - LocationProviderInterface mGpsLocationProvider; + // --- fields below are protected by mWakeLock --- + private int mPendingBroadcasts; - // Handler messages - private static final int MESSAGE_LOCATION_CHANGED = 1; - private static final int MESSAGE_PACKAGE_UPDATED = 2; + // --- fields below are protected by mLock --- + // Set of providers that are explicitly enabled + private final Set<String> mEnabledProviders = new HashSet<String>(); - // wakelock variables - private final static String WAKELOCK_KEY = "LocationManagerService"; - private PowerManager.WakeLock mWakeLock = null; - private int mPendingBroadcasts; - - /** - * List of all receivers. - */ - private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>(); + // Set of providers that are explicitly disabled + private final Set<String> mDisabledProviders = new HashSet<String>(); + // Mock (test) providers + private final HashMap<String, MockProvider> mMockProviders = + new HashMap<String, MockProvider>(); - /** - * List of location providers. - */ + // all receivers + private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>(); + + // currently installed providers (with mocks replacing real providers) private final ArrayList<LocationProviderInterface> mProviders = - new ArrayList<LocationProviderInterface>(); - private final HashMap<String, LocationProviderInterface> mProvidersByName - = new HashMap<String, LocationProviderInterface>(); + new ArrayList<LocationProviderInterface>(); - /** - * Object used internally for synchronization - */ - private final Object mLock = new Object(); + // real providers, saved here when mocked out + private final HashMap<String, LocationProviderInterface> mRealProviders = + new HashMap<String, LocationProviderInterface>(); - /** - * Mapping from provider name to all its UpdateRecords - */ - private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider = - new HashMap<String,ArrayList<UpdateRecord>>(); + // mapping from provider name to provider + private final HashMap<String, LocationProviderInterface> mProvidersByName = + new HashMap<String, LocationProviderInterface>(); - /** - * Temporary filled in when computing min time for a provider. Access is - * protected by global lock mLock. - */ - private final WorkSource mTmpWorkSource = new WorkSource(); + // mapping from provider name to all its UpdateRecords + private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider = + new HashMap<String, ArrayList<UpdateRecord>>(); + + // mapping from provider name to last known location + private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>(); + + // all providers that operate over proxy, for authorizing incoming location + private final ArrayList<LocationProviderProxy> mProxyProviders = + new ArrayList<LocationProviderProxy>(); + + public LocationManagerService(Context context) { + super(); + mContext = context; + + if (D) Log.d(TAG, "Constructed"); + + // most startup is deferred until systemReady() + } + + public void systemReady() { + Thread thread = new Thread(null, this, THREAD_NAME); + thread.start(); + } - // Proximity listeners - private Receiver mProximityReceiver = null; - private ILocationListener mProximityListener = null; - private HashMap<PendingIntent,ProximityAlert> mProximityAlerts = - new HashMap<PendingIntent,ProximityAlert>(); - private HashSet<ProximityAlert> mProximitiesEntered = - new HashSet<ProximityAlert>(); + @Override + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + Looper.prepare(); + mLocationHandler = new LocationWorkerHandler(); + init(); + Looper.loop(); + } + + private void init() { + if (D) Log.d(TAG, "init()"); + + PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); + mPackageManager = mContext.getPackageManager(); - // Last known location for each provider - private HashMap<String,Location> mLastKnownLocation = - new HashMap<String,Location>(); + synchronized (mLock) { + loadProvidersLocked(); + } + mBlacklist = new LocationBlacklist(mContext, mLocationHandler); + mBlacklist.init(); + mGeofenceManager = new GeofenceManager(mContext, mBlacklist); + mLocationFudger = new LocationFudger(); + + // listen for settings changes + ContentResolver resolver = mContext.getContentResolver(); + Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null, + "(" + NameValueTable.NAME + "=?)", + new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null); + ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true, + mLocationHandler); + settingsCursor.close(); + query.addObserver(this); + mPackageMonitor.register(mContext, Looper.myLooper(), true); + } + + private void loadProvidersLocked() { + if (GpsLocationProvider.isSupported()) { + // Create a gps location provider + GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this); + mGpsStatusProvider = gpsProvider.getGpsStatusProvider(); + mNetInitiatedListener = gpsProvider.getNetInitiatedListener(); + addProviderLocked(gpsProvider); + mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider); + } - private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; + // create a passive location provider, which is always enabled + PassiveProvider passiveProvider = new PassiveProvider(this); + addProviderLocked(passiveProvider); + mEnabledProviders.add(passiveProvider.getName()); + mPassiveProvider = passiveProvider; + + /* + Load package name(s) containing location provider support. + These packages can contain services implementing location providers: + Geocoder Provider, Network Location Provider, and + Fused Location Provider. They will each be searched for + service components implementing these providers. + The location framework also has support for installation + of new location providers at run-time. The new package does not + have to be explicitly listed here, however it must have a signature + that matches the signature of at least one package on this list. + */ + Resources resources = mContext.getResources(); + ArrayList<String> providerPackageNames = new ArrayList<String>(); + String[] pkgs1 = resources.getStringArray( + com.android.internal.R.array.config_locationProviderPackageNames); + String[] pkgs2 = resources.getStringArray( + com.android.internal.R.array.config_overlay_locationProviderPackageNames); + if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1)); + if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2)); + if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1)); + if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2)); + + // bind to network provider + LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind( + mContext, + LocationManager.NETWORK_PROVIDER, + NETWORK_LOCATION_SERVICE_ACTION, + providerPackageNames, mLocationHandler); + if (networkProvider != null) { + mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider); + mProxyProviders.add(networkProvider); + addProviderLocked(networkProvider); + } else { + Slog.w(TAG, "no network location provider found"); + } + + // bind to fused provider + LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind( + mContext, + LocationManager.FUSED_PROVIDER, + FUSED_LOCATION_SERVICE_ACTION, + providerPackageNames, mLocationHandler); + if (fusedLocationProvider != null) { + addProviderLocked(fusedLocationProvider); + mProxyProviders.add(fusedLocationProvider); + mEnabledProviders.add(fusedLocationProvider.getName()); + } else { + Slog.e(TAG, "no fused location provider found", + new IllegalStateException("Location service needs a fused location provider")); + } - // for prefix blacklist - private String[] mWhitelist = new String[0]; - private String[] mBlacklist = new String[0]; + // bind to geocoder provider + mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames); + if (mGeocodeProvider == null) { + Slog.e(TAG, "no geocoder provider found"); + } - // for Settings change notification - private ContentQueryMap mSettings; + updateProvidersLocked(); + } /** * A wrapper class holding either an ILocationListener or a PendingIntent to receive * location updates. */ private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished { + final int mUid; // uid of receiver + final int mPid; // pid of receiver + final String mPackageName; // package name of receiver + final String mPermission; // best permission that receiver has + final ILocationListener mListener; final PendingIntent mPendingIntent; final Object mKey; + final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>(); - final String mPackageName; int mPendingBroadcasts; - String mRequiredPermissions; - Receiver(ILocationListener listener, String packageName) { + Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid, + String packageName) { mListener = listener; - mPendingIntent = null; - mKey = listener.asBinder(); - mPackageName = packageName; - } - - Receiver(PendingIntent intent, String packageName) { mPendingIntent = intent; - mListener = null; - mKey = intent; + if (listener != null) { + mKey = listener.asBinder(); + } else { + mKey = intent; + } + mPermission = checkPermission(); + mUid = uid; + mPid = pid; mPackageName = packageName; } @Override public boolean equals(Object otherObj) { if (otherObj instanceof Receiver) { - return mKey.equals( - ((Receiver)otherObj).mKey); + return mKey.equals(((Receiver)otherObj).mKey); } return false; } @@ -250,18 +350,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public String toString() { - String result; + StringBuilder s = new StringBuilder(); + s.append("Reciever["); + s.append(Integer.toHexString(System.identityHashCode(this))); if (mListener != null) { - result = "Receiver{" - + Integer.toHexString(System.identityHashCode(this)) - + " Listener " + mKey + "}"; + s.append(" listener"); } else { - result = "Receiver{" - + Integer.toHexString(System.identityHashCode(this)) - + " Intent " + mKey + "}"; + s.append(" intent"); } - result += "mUpdateRecords: " + mUpdateRecords; - return result; + for (String p : mUpdateRecords.keySet()) { + s.append(" ").append(mUpdateRecords.get(p).toString()); + } + s.append("]"); + return s.toString(); } public boolean isListener() { @@ -279,13 +380,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run throw new IllegalStateException("Request for non-existent listener"); } - public PendingIntent getPendingIntent() { - if (mPendingIntent != null) { - return mPendingIntent; - } - throw new IllegalStateException("Request for non-existent intent"); - } - public boolean callStatusChangedLocked(String provider, int status, Bundle extras) { if (mListener != null) { try { @@ -293,11 +387,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mListener.onStatusChanged(provider, status, extras); - if (mListener != mProximityListener) { - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + // call this after broadcasting so we do not increment + // if we throw an exeption. + incrementPendingBroadcastsLocked(); } } catch (RemoteException e) { return false; @@ -311,7 +403,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler, - mRequiredPermissions); + mPermission); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -330,11 +422,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mListener.onLocationChanged(location); - if (mListener != mProximityListener) { - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + // call this after broadcasting so we do not increment + // if we throw an exeption. + incrementPendingBroadcastsLocked(); } } catch (RemoteException e) { return false; @@ -347,7 +437,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, - mRequiredPermissions); + mPermission); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -370,11 +460,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } else { mListener.onProviderDisabled(provider); } - if (mListener != mProximityListener) { - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcastsLocked(); - } + // call this after broadcasting so we do not increment + // if we throw an exeption. + incrementPendingBroadcastsLocked(); } } catch (RemoteException e) { return false; @@ -387,7 +475,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler, - mRequiredPermissions); + mPermission); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -401,9 +489,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public void binderDied() { - if (LOCAL_LOGV) { - Slog.v(TAG, "Location listener died"); - } + if (D) Log.d(TAG, "Location listener died"); + synchronized (mLock) { removeUpdatesLocked(this); } @@ -415,6 +502,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, String resultData, Bundle resultExtras) { synchronized (this) { @@ -437,6 +525,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public void locationCallbackFinished(ILocationListener listener) { //Do not use getReceiver here as that will add the ILocationListener to //the receiver list if it is not found. If it is not found then the @@ -454,198 +543,25 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private final class SettingsObserver implements Observer { - public void update(Observable o, Object arg) { - synchronized (mLock) { - updateProvidersLocked(); - } + /** Settings Observer callback */ + @Override + public void update(Observable o, Object arg) { + synchronized (mLock) { + updateProvidersLocked(); } } - private void addProvider(LocationProviderInterface provider) { + private void addProviderLocked(LocationProviderInterface provider) { mProviders.add(provider); mProvidersByName.put(provider.getName(), provider); } - private void removeProvider(LocationProviderInterface provider) { + private void removeProviderLocked(LocationProviderInterface provider) { + provider.disable(); mProviders.remove(provider); mProvidersByName.remove(provider.getName()); } - private void loadProviders() { - synchronized (mLock) { - if (sProvidersLoaded) { - return; - } - - // Load providers - loadProvidersLocked(); - sProvidersLoaded = true; - } - } - - private void loadProvidersLocked() { - try { - _loadProvidersLocked(); - } catch (Exception e) { - Slog.e(TAG, "Exception loading providers:", e); - } - } - - private void _loadProvidersLocked() { - // Attempt to load "real" providers first - if (GpsLocationProvider.isSupported()) { - // Create a gps location provider - GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this); - mGpsStatusProvider = gpsProvider.getGpsStatusProvider(); - mNetInitiatedListener = gpsProvider.getNetInitiatedListener(); - addProvider(gpsProvider); - mGpsLocationProvider = gpsProvider; - } - - // create a passive location provider, which is always enabled - PassiveProvider passiveProvider = new PassiveProvider(this); - addProvider(passiveProvider); - mEnabledProviders.add(passiveProvider.getName()); - - // initialize external network location and geocoder services. - // The initial value of mNetworkLocationProviderPackageName and - // mGeocodeProviderPackageName is just used to determine what - // signatures future mNetworkLocationProviderPackageName and - // mGeocodeProviderPackageName packages must have. So alternate - // providers can be installed under a different package name - // so long as they have the same signature as the original - // provider packages. - if (mNetworkLocationProviderPackageName != null) { - String packageName = findBestPackage(LocationProviderProxy.SERVICE_ACTION, - mNetworkLocationProviderPackageName); - if (packageName != null) { - mNetworkLocationProvider = new LocationProviderProxy(mContext, - LocationManager.NETWORK_PROVIDER, - packageName, mLocationHandler); - mNetworkLocationProviderPackageName = packageName; - addProvider(mNetworkLocationProvider); - } - } - if (mGeocodeProviderPackageName != null) { - String packageName = findBestPackage(GeocoderProxy.SERVICE_ACTION, - mGeocodeProviderPackageName); - if (packageName != null) { - mGeocodeProvider = new GeocoderProxy(mContext, packageName); - mGeocodeProviderPackageName = packageName; - } - } - - updateProvidersLocked(); - } - - /** - * Pick the best (network location provider or geocode provider) package. - * The best package: - * - implements serviceIntentName - * - has signatures that match that of sigPackageName - * - has the highest version value in a meta-data field in the service component - */ - String findBestPackage(String serviceIntentName, String sigPackageName) { - Intent intent = new Intent(serviceIntentName); - List<ResolveInfo> infos = mPackageManager.queryIntentServices(intent, - PackageManager.GET_META_DATA); - if (infos == null) return null; - - int bestVersion = Integer.MIN_VALUE; - String bestPackage = null; - for (ResolveInfo info : infos) { - String packageName = info.serviceInfo.packageName; - // check signature - if (mPackageManager.checkSignatures(packageName, sigPackageName) != - PackageManager.SIGNATURE_MATCH) { - Slog.w(TAG, packageName + " implements " + serviceIntentName + - " but its signatures don't match those in " + sigPackageName + - ", ignoring"); - continue; - } - // read version - int version = 0; - if (info.serviceInfo.metaData != null) { - version = info.serviceInfo.metaData.getInt("version", 0); - } - if (LOCAL_LOGV) Slog.v(TAG, packageName + " implements " + serviceIntentName + - " with version " + version); - if (version > bestVersion) { - bestVersion = version; - bestPackage = packageName; - } - } - - return bestPackage; - } - - /** - * @param context the context that the LocationManagerService runs in - */ - public LocationManagerService(Context context) { - super(); - mContext = context; - Resources resources = context.getResources(); - - mNetworkLocationProviderPackageName = resources.getString( - com.android.internal.R.string.config_networkLocationProviderPackageName); - mGeocodeProviderPackageName = resources.getString( - com.android.internal.R.string.config_geocodeProviderPackageName); - - mPackageMonitor.register(context, null, true); - - if (LOCAL_LOGV) { - Slog.v(TAG, "Constructed LocationManager Service"); - } - } - - void systemReady() { - // we defer starting up the service until the system is ready - Thread thread = new Thread(null, this, "LocationManagerService"); - thread.start(); - } - - private void initialize() { - // Create a wake lock, needs to be done before calling loadProviders() below - PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); - mPackageManager = mContext.getPackageManager(); - - // Load providers - loadProviders(); - loadBlacklist(); - - // Register for Network (Wifi or Mobile) updates - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - // Register for Package Manager updates - intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); - mContext.registerReceiver(mBroadcastReceiver, intentFilter); - IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(mBroadcastReceiver, sdFilter); - - // listen for settings changes - ContentResolver resolver = mContext.getContentResolver(); - Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null, - "(" + Settings.System.NAME + "=?)", - new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, - null); - mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler); - SettingsObserver settingsObserver = new SettingsObserver(); - mSettings.addObserver(settingsObserver); - } - - public void run() - { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - Looper.prepare(); - mLocationHandler = new LocationWorkerHandler(); - initialize(); - Looper.loop(); - } private boolean isAllowedBySettingsLocked(String provider) { if (mEnabledProviders.contains(provider)) { @@ -660,317 +576,131 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return Settings.Secure.isLocationProviderEnabled(resolver, provider); } - private String checkPermissionsSafe(String provider, String lastPermission) { - if (LocationManager.GPS_PROVIDER.equals(provider) - || LocationManager.PASSIVE_PROVIDER.equals(provider)) { - if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION permission"); - } - return ACCESS_FINE_LOCATION; - } - - // Assume any other provider requires the coarse or fine permission. - if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) - == PackageManager.PERMISSION_GRANTED) { - return ACCESS_FINE_LOCATION.equals(lastPermission) - ? lastPermission : ACCESS_COARSE_LOCATION; - } - if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED) { + /** + * Throw SecurityException if caller has neither COARSE or FINE. + * Otherwise, return the best permission. + */ + private String checkPermission() { + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) == + PackageManager.PERMISSION_GRANTED) { return ACCESS_FINE_LOCATION; + } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == + PackageManager.PERMISSION_GRANTED) { + return ACCESS_COARSE_LOCATION; } - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); - } - - private boolean isAllowedProviderSafe(String provider) { - if ((LocationManager.GPS_PROVIDER.equals(provider) - || LocationManager.PASSIVE_PROVIDER.equals(provider)) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - return false; - } - if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - return false; - } - - return true; + throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" + + "ACCESS_FINE_LOCATION permission"); } + /** + * Returns all providers by name, including passive, but excluding + * fused. + */ + @Override public List<String> getAllProviders() { - try { - synchronized (mLock) { - return _getAllProvidersLocked(); + checkPermission(); + + 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; + } + out.add(name); } - } catch (SecurityException se) { - throw se; - } catch (Exception e) { - Slog.e(TAG, "getAllProviders got exception:", e); - return null; } - } - private List<String> _getAllProvidersLocked() { - if (LOCAL_LOGV) { - Slog.v(TAG, "getAllProviders"); - } - ArrayList<String> out = new ArrayList<String>(mProviders.size()); - for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderInterface p = mProviders.get(i); - out.add(p.getName()); - } + if (D) Log.d(TAG, "getAllProviders()=" + out); return out; } + /** + * Return all providers by name, that match criteria and are optionally + * enabled. + * Can return passive provider, but never returns fused provider. + */ + @Override public List<String> getProviders(Criteria criteria, boolean enabledOnly) { - try { - synchronized (mLock) { - return _getProvidersLocked(criteria, enabledOnly); - } - } catch (SecurityException se) { - throw se; - } catch (Exception e) { - Slog.e(TAG, "getProviders got exception:", e); - return null; - } - } + checkPermission(); - private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) { - if (LOCAL_LOGV) { - Slog.v(TAG, "getProviders"); - } - ArrayList<String> out = new ArrayList<String>(mProviders.size()); - for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderInterface p = mProviders.get(i); - String name = p.getName(); - if (isAllowedProviderSafe(name)) { + 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 (enabledOnly && !isAllowedBySettingsLocked(name)) { continue; } - if (criteria != null && !p.meetsCriteria(criteria)) { + if (criteria != null && !LocationProvider.propertiesMeetCriteria( + name, provider.getProperties(), criteria)) { continue; } out.add(name); } } + + if (D) Log.d(TAG, "getProviders()=" + out); return out; } /** - * Returns the next looser power requirement, in the sequence: - * - * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT + * Return the name of the best provider given a Criteria object. + * This method has been deprecated from the public API, + * and the whole LoactionProvider (including #meetsCriteria) + * has been deprecated as well. So this method now uses + * some simplified logic. */ - private int nextPower(int power) { - switch (power) { - case Criteria.POWER_LOW: - return Criteria.POWER_MEDIUM; - case Criteria.POWER_MEDIUM: - return Criteria.POWER_HIGH; - case Criteria.POWER_HIGH: - return Criteria.NO_REQUIREMENT; - case Criteria.NO_REQUIREMENT: - default: - return Criteria.NO_REQUIREMENT; - } - } + @Override + public String getBestProvider(Criteria criteria, boolean enabledOnly) { + String result = null; + checkPermission(); - /** - * Returns the next looser accuracy requirement, in the sequence: - * - * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT - */ - private int nextAccuracy(int accuracy) { - if (accuracy == Criteria.ACCURACY_FINE) { - return Criteria.ACCURACY_COARSE; - } else { - return Criteria.NO_REQUIREMENT; + List<String> providers = getProviders(criteria, enabledOnly); + if (providers.size() < 1) { + result = pickBest(providers); + if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result); + return result; } - } - - private class LpPowerComparator implements Comparator<LocationProviderInterface> { - public int compare(LocationProviderInterface l1, LocationProviderInterface l2) { - // Smaller is better - return (l1.getPowerRequirement() - l2.getPowerRequirement()); - } - - public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) { - return (l1.getPowerRequirement() == l2.getPowerRequirement()); - } - } - - private class LpAccuracyComparator implements Comparator<LocationProviderInterface> { - public int compare(LocationProviderInterface l1, LocationProviderInterface l2) { - // Smaller is better - return (l1.getAccuracy() - l2.getAccuracy()); - } - - public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) { - return (l1.getAccuracy() == l2.getAccuracy()); - } - } - - private class LpCapabilityComparator implements Comparator<LocationProviderInterface> { - - private static final int ALTITUDE_SCORE = 4; - private static final int BEARING_SCORE = 4; - private static final int SPEED_SCORE = 4; - - private int score(LocationProviderInterface p) { - return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) + - (p.supportsBearing() ? BEARING_SCORE : 0) + - (p.supportsSpeed() ? SPEED_SCORE : 0); + providers = getProviders(null, enabledOnly); + if (providers.size() >= 1) { + result = pickBest(providers); + if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result); + return result; } - public int compare(LocationProviderInterface l1, LocationProviderInterface l2) { - return (score(l2) - score(l1)); // Bigger is better - } - - public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) { - return (score(l1) == score(l2)); - } + if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result); + return null; } - private LocationProviderInterface best(List<String> providerNames) { - ArrayList<LocationProviderInterface> providers; - synchronized (mLock) { - providers = new ArrayList<LocationProviderInterface>(providerNames.size()); - for (String name : providerNames) { - providers.add(mProvidersByName.get(name)); - } - } - - if (providers.size() < 2) { - return providers.get(0); - } - - // First, sort by power requirement - Collections.sort(providers, new LpPowerComparator()); - int power = providers.get(0).getPowerRequirement(); - if (power < providers.get(1).getPowerRequirement()) { + private String pickBest(List<String> providers) { + if (providers.contains(LocationManager.NETWORK_PROVIDER)) { + return LocationManager.NETWORK_PROVIDER; + } else if (providers.contains(LocationManager.GPS_PROVIDER)) { + return LocationManager.GPS_PROVIDER; + } else { return providers.get(0); } - - int idx, size; - - ArrayList<LocationProviderInterface> tmp = new ArrayList<LocationProviderInterface>(); - idx = 0; - size = providers.size(); - while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) { - tmp.add(providers.get(idx)); - idx++; - } - - // Next, sort by accuracy - Collections.sort(tmp, new LpAccuracyComparator()); - int acc = tmp.get(0).getAccuracy(); - if (acc < tmp.get(1).getAccuracy()) { - return tmp.get(0); - } - - ArrayList<LocationProviderInterface> tmp2 = new ArrayList<LocationProviderInterface>(); - idx = 0; - size = tmp.size(); - while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) { - tmp2.add(tmp.get(idx)); - idx++; - } - - // Finally, sort by capability "score" - Collections.sort(tmp2, new LpCapabilityComparator()); - return tmp2.get(0); - } - - /** - * Returns the name of the provider that best meets the given criteria. Only providers - * that are permitted to be accessed by the calling activity will be - * returned. If several providers meet the criteria, the one with the best - * accuracy is returned. If no provider meets the criteria, - * the criteria are loosened in the following sequence: - * - * <ul> - * <li> power requirement - * <li> accuracy - * <li> bearing - * <li> speed - * <li> altitude - * </ul> - * - * <p> Note that the requirement on monetary cost is not removed - * in this process. - * - * @param criteria the criteria that need to be matched - * @param enabledOnly if true then only a provider that is currently enabled is returned - * @return name of the provider that best matches the requirements - */ - public String getBestProvider(Criteria criteria, boolean enabledOnly) { - List<String> goodProviders = getProviders(criteria, enabledOnly); - if (!goodProviders.isEmpty()) { - return best(goodProviders).getName(); - } - - // Make a copy of the criteria that we can modify - criteria = new Criteria(criteria); - - // Loosen power requirement - int power = criteria.getPowerRequirement(); - while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) { - power = nextPower(power); - criteria.setPowerRequirement(power); - goodProviders = getProviders(criteria, enabledOnly); - } - if (!goodProviders.isEmpty()) { - return best(goodProviders).getName(); - } - - // Loosen accuracy requirement - int accuracy = criteria.getAccuracy(); - while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) { - accuracy = nextAccuracy(accuracy); - criteria.setAccuracy(accuracy); - goodProviders = getProviders(criteria, enabledOnly); - } - if (!goodProviders.isEmpty()) { - return best(goodProviders).getName(); - } - - // Remove bearing requirement - criteria.setBearingRequired(false); - goodProviders = getProviders(criteria, enabledOnly); - if (!goodProviders.isEmpty()) { - return best(goodProviders).getName(); - } - - // Remove speed requirement - criteria.setSpeedRequired(false); - goodProviders = getProviders(criteria, enabledOnly); - if (!goodProviders.isEmpty()) { - return best(goodProviders).getName(); - } - - // Remove altitude requirement - criteria.setAltitudeRequired(false); - goodProviders = getProviders(criteria, enabledOnly); - if (!goodProviders.isEmpty()) { - return best(goodProviders).getName(); - } - - return null; } + @Override public boolean providerMeetsCriteria(String provider, Criteria criteria) { + checkPermission(); + LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { throw new IllegalArgumentException("provider=" + provider); } - return p.meetsCriteria(criteria); + + boolean result = LocationProvider.propertiesMeetCriteria( + p.getName(), p.getProperties(), criteria); + if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result); + return result; } private void updateProvidersLocked() { @@ -997,16 +727,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run int listeners = 0; LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) { - return; - } + if (p == null) return; ArrayList<Receiver> deadReceivers = null; - + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records != null) { final int N = records.size(); - for (int i=0; i<N; i++) { + 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)) { @@ -1020,67 +748,74 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } if (deadReceivers != null) { - for (int i=deadReceivers.size()-1; i>=0; i--) { + for (int i = deadReceivers.size() - 1; i >= 0; i--) { removeUpdatesLocked(deadReceivers.get(i)); } } - + if (enabled) { p.enable(); if (listeners > 0) { - p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource); - p.enableLocationTracking(true); + applyRequirementsLocked(provider); } } else { - p.enableLocationTracking(false); p.disable(); } } - private long getMinTimeLocked(String provider) { - long minTime = Long.MAX_VALUE; + private void applyRequirementsLocked(String provider) { + LocationProviderInterface p = mProvidersByName.get(provider); + if (p == null) return; + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); - mTmpWorkSource.clear(); + WorkSource worksource = new WorkSource(); + ProviderRequest providerRequest = new ProviderRequest(); + if (records != null) { - for (int i=records.size()-1; i>=0; i--) { - UpdateRecord ur = records.get(i); - long curTime = ur.mMinTime; - if (curTime < minTime) { - minTime = curTime; + for (UpdateRecord record : records) { + LocationRequest locationRequest = record.mRequest; + + providerRequest.locationRequests.add(locationRequest); + if (locationRequest.getInterval() < providerRequest.interval) { + providerRequest.reportLocation = true; + providerRequest.interval = locationRequest.getInterval(); } } - long inclTime = (minTime*3)/2; - for (int i=records.size()-1; i>=0; i--) { - UpdateRecord ur = records.get(i); - if (ur.mMinTime <= inclTime) { - mTmpWorkSource.add(ur.mUid); + + if (providerRequest.reportLocation) { + // calculate who to blame for power + // This is somewhat arbitrary. We pick a threshold interval + // that is slightly higher that the minimum interval, and + // spread the blame across all applications with a request + // 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); + } } } } - return minTime; + + if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest); + p.setRequest(providerRequest, worksource); } private class UpdateRecord { final String mProvider; + final LocationRequest mRequest; final Receiver mReceiver; - final long mMinTime; - final float mMinDistance; - final boolean mSingleShot; - final int mUid; Location mLastFixBroadcast; long mLastStatusBroadcast; /** * Note: must be constructed with lock held. */ - UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot, - Receiver receiver, int uid) { + UpdateRecord(String provider, LocationRequest request, Receiver receiver) { mProvider = provider; + mRequest = request; mReceiver = receiver; - mMinTime = minTime; - mMinDistance = minDistance; - mSingleShot = singleShot; - mUid = uid; ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records == null) { @@ -1096,45 +831,49 @@ public class LocationManagerService extends ILocationManager.Stub implements Run * Method to be called when a record will no longer be used. Calling this multiple times * must have the same effect as calling it once. */ - void disposeLocked() { - ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider); - if (records != null) { - records.remove(this); + void disposeLocked(boolean removeReceiver) { + // remove from mRecordsByProvider + ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider); + if (globalRecords != null) { + globalRecords.remove(this); + } + + if (!removeReceiver) return; // the caller will handle the rest + + // remove from Receiver#mUpdateRecords + HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords; + if (receiverRecords != null) { + receiverRecords.remove(this.mProvider); + + // and also remove the Receiver if it has no more update records + if (removeReceiver && receiverRecords.size() == 0) { + removeUpdatesLocked(mReceiver); + } } } @Override public String toString() { - return "UpdateRecord{" - + Integer.toHexString(System.identityHashCode(this)) - + " mProvider: " + mProvider + " mUid: " + mUid + "}"; - } - - void dump(PrintWriter pw, String prefix) { - pw.println(prefix + this); - pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver); - pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance); - pw.println(prefix + "mSingleShot=" + mSingleShot); - pw.println(prefix + "mUid=" + mUid); - pw.println(prefix + "mLastFixBroadcast:"); - if (mLastFixBroadcast != null) { - mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " "); - } - pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast); + StringBuilder s = new StringBuilder(); + s.append("UpdateRecord["); + s.append(mProvider); + s.append(' ').append(mReceiver.mPackageName).append('('); + s.append(mReceiver.mUid).append(')'); + s.append(' ').append(mRequest); + s.append(']'); + return s.toString(); } } - private Receiver getReceiver(ILocationListener listener, String packageName) { + private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) { IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); if (receiver == null) { - receiver = new Receiver(listener, packageName); + receiver = new Receiver(listener, null, pid, uid, packageName); mReceivers.put(binder, receiver); try { - if (receiver.isListener()) { - receiver.getListener().asBinder().linkToDeath(receiver, 0); - } + receiver.getListener().asBinder().linkToDeath(receiver, 0); } catch (RemoteException e) { Slog.e(TAG, "linkToDeath failed:", e); return null; @@ -1143,251 +882,256 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return receiver; } - private Receiver getReceiver(PendingIntent intent, String packageName) { + private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) { Receiver receiver = mReceivers.get(intent); if (receiver == null) { - receiver = new Receiver(intent, packageName); + receiver = new Receiver(null, intent, pid, uid, packageName); mReceivers.put(intent, receiver); } return receiver; } - private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) { - ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); - if (records != null) { - for (int i = records.size() - 1; i >= 0; i--) { - UpdateRecord record = records.get(i); - if (record.mUid == uid && record.mReceiver != excludedReceiver) { - return true; - } - } + private String checkPermissionAndRequest(LocationRequest request) { + String perm = checkPermission(); + + if (ACCESS_COARSE_LOCATION.equals(perm)) { + switch (request.getQuality()) { + case LocationRequest.ACCURACY_FINE: + request.setQuality(LocationRequest.ACCURACY_BLOCK); + break; + case LocationRequest.POWER_HIGH: + request.setQuality(LocationRequest.POWER_LOW); + break; + } + // throttle + if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) { + request.setInterval(LocationFudger.FASTEST_INTERVAL_MS); + } + if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) { + request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS); + } } - for (ProximityAlert alert : mProximityAlerts.values()) { - if (alert.mUid == uid) { - return true; - } + // make getFastestInterval() the minimum of interval and fastest interval + if (request.getFastestInterval() > request.getInterval()) { + request.setFastestInterval(request.getInterval()); } - return false; + return perm; } - public void requestLocationUpdates(String provider, Criteria criteria, - long minTime, float minDistance, boolean singleShot, ILocationListener listener, - String packageName) { - checkPackageName(Binder.getCallingUid(), packageName); - if (criteria != null) { - // FIXME - should we consider using multiple providers simultaneously - // rather than only the best one? - // Should we do anything different for single shot fixes? - provider = getBestProvider(criteria, true); - if (provider == null) { - throw new IllegalArgumentException("no providers found for criteria"); - } + private void checkPackageName(String packageName) { + if (packageName == null) { + throw new SecurityException("invalid package name: " + packageName); } - try { - synchronized (mLock) { - requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot, - getReceiver(listener, packageName)); - } - } catch (SecurityException se) { - throw se; - } catch (IllegalArgumentException iae) { - throw iae; - } catch (Exception e) { - Slog.e(TAG, "requestUpdates got exception:", e); + int uid = Binder.getCallingUid(); + String[] packages = mPackageManager.getPackagesForUid(uid); + if (packages == null) { + throw new SecurityException("invalid UID " + uid); + } + for (String pkg : packages) { + if (packageName.equals(pkg)) return; } + throw new SecurityException("invalid package name: " + packageName); } - void validatePendingIntent(PendingIntent intent) { - if (intent.isTargetedToPackage()) { - return; + private void checkPendingIntent(PendingIntent intent) { + if (intent == null) { + throw new IllegalArgumentException("invalid pending intent: " + intent); } - Slog.i(TAG, "Given Intent does not require a specific package: " - + intent); - // XXX we should really throw a security exception, if the caller's - // targetSdkVersion is high enough. - //throw new SecurityException("Given Intent does not require a specific package: " - // + intent); } - public void requestLocationUpdatesPI(String provider, Criteria criteria, - long minTime, float minDistance, boolean singleShot, PendingIntent intent, - String packageName) { - checkPackageName(Binder.getCallingUid(), packageName); - validatePendingIntent(intent); - if (criteria != null) { - // FIXME - should we consider using multiple providers simultaneously - // rather than only the best one? - // Should we do anything different for single shot fixes? - provider = getBestProvider(criteria, true); - if (provider == null) { - throw new IllegalArgumentException("no providers found for criteria"); - } - } - try { - synchronized (mLock) { - requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot, - getReceiver(intent, packageName)); - } - } catch (SecurityException se) { - throw se; - } catch (IllegalArgumentException iae) { - throw iae; - } catch (Exception e) { - Slog.e(TAG, "requestUpdates got exception:", e); + private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent, + int pid, int uid, String packageName) { + if (intent == null && listener == null) { + throw new IllegalArgumentException("need eiter listener or intent"); + } else if (intent != null && listener != null) { + throw new IllegalArgumentException("cannot register both listener and intent"); + } else if (intent != null) { + checkPendingIntent(intent); + return getReceiver(intent, pid, uid, packageName); + } else { + return getReceiver(listener, pid, uid, packageName); } } - private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance, - boolean singleShot, Receiver receiver) { + @Override + public void requestLocationUpdates(LocationRequest request, ILocationListener listener, + PendingIntent intent, String packageName) { + if (request == null) request = DEFAULT_LOCATION_REQUEST; + checkPackageName(packageName); + checkPermissionAndRequest(request); - LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) { - throw new IllegalArgumentException("requested provider " + provider + - " doesn't exisit"); - } - receiver.mRequiredPermissions = checkPermissionsSafe(provider, - receiver.mRequiredPermissions); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName); - // so wakelock calls will succeed - final int callingPid = Binder.getCallingPid(); - final int callingUid = Binder.getCallingUid(); - boolean newUid = !providerHasListener(provider, callingUid, null); + // providers may use public location API's, need to clear identity long identity = Binder.clearCallingIdentity(); try { - UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot, - receiver, callingUid); - UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r); - if (oldRecord != null) { - oldRecord.disposeLocked(); - } - - if (newUid) { - p.addListener(callingUid); - } - - boolean isProviderEnabled = isAllowedBySettingsLocked(provider); - if (isProviderEnabled) { - long minTimeForProvider = getMinTimeLocked(provider); - Slog.i(TAG, "request " + provider + " (pid " + callingPid + ") " + minTime + - " " + minTimeForProvider + (singleShot ? " (singleshot)" : "")); - p.setMinTime(minTimeForProvider, mTmpWorkSource); - // try requesting single shot if singleShot is true, and fall back to - // regular location tracking if requestSingleShotFix() is not supported - if (!singleShot || !p.requestSingleShotFix()) { - p.enableLocationTracking(true); - } - } else { - // Notify the listener that updates are currently disabled - receiver.callProviderEnabledLocked(provider, false); - } - if (LOCAL_LOGV) { - Slog.v(TAG, "_requestLocationUpdates: provider = " + provider + " listener = " + receiver); + synchronized (mLock) { + requestLocationUpdatesLocked(request, recevier, pid, uid, packageName); } } finally { Binder.restoreCallingIdentity(identity); } } - public void removeUpdates(ILocationListener listener, String packageName) { - try { - synchronized (mLock) { - removeUpdatesLocked(getReceiver(listener, packageName)); - } - } catch (SecurityException se) { - throw se; - } catch (IllegalArgumentException iae) { - throw iae; - } catch (Exception e) { - Slog.e(TAG, "removeUpdates got exception:", e); + private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver, + int pid, int uid, String packageName) { + // Figure out the provider. Either its explicitly request (legacy use cases), or + // use the fused provider + if (request == null) request = DEFAULT_LOCATION_REQUEST; + String name = request.getProvider(); + if (name == null) name = LocationManager.FUSED_PROVIDER; + LocationProviderInterface provider = mProvidersByName.get(name); + if (provider == null) { + throw new IllegalArgumentException("provider doesn't exisit: " + provider); + } + + Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " + + name + " " + request + " from " + packageName + "(" + uid + ")"); + + UpdateRecord record = new UpdateRecord(name, request, receiver); + UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record); + if (oldRecord != null) { + oldRecord.disposeLocked(false); + } + + boolean isProviderEnabled = isAllowedBySettingsLocked(name); + if (isProviderEnabled) { + applyRequirementsLocked(name); + } else { + // Notify the listener that updates are currently disabled + receiver.callProviderEnabledLocked(name, false); } } - public void removeUpdatesPI(PendingIntent intent, String packageName) { + @Override + public void removeUpdates(ILocationListener listener, PendingIntent intent, + String packageName) { + checkPackageName(packageName); + checkPermission(); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName); + + // providers may use public location API's, need to clear identity + long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - removeUpdatesLocked(getReceiver(intent, packageName)); + removeUpdatesLocked(receiver); } - } catch (SecurityException se) { - throw se; - } catch (IllegalArgumentException iae) { - throw iae; - } catch (Exception e) { - Slog.e(TAG, "removeUpdates got exception:", e); + } finally { + Binder.restoreCallingIdentity(identity); } } private void removeUpdatesLocked(Receiver receiver) { - if (LOCAL_LOGV) { - Slog.v(TAG, "_removeUpdates: listener = " + receiver); - } + Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver))); - // so wakelock calls will succeed - final int callingPid = Binder.getCallingPid(); - final int callingUid = Binder.getCallingUid(); - long identity = Binder.clearCallingIdentity(); - try { - if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) { - receiver.getListener().asBinder().unlinkToDeath(receiver, 0); - synchronized(receiver) { - if(receiver.mPendingBroadcasts > 0) { - decrementPendingBroadcasts(); - receiver.mPendingBroadcasts = 0; - } + if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) { + receiver.getListener().asBinder().unlinkToDeath(receiver, 0); + synchronized (receiver) { + if (receiver.mPendingBroadcasts > 0) { + decrementPendingBroadcasts(); + receiver.mPendingBroadcasts = 0; } } + } - // Record which providers were associated with this listener - HashSet<String> providers = new HashSet<String>(); - HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords; - if (oldRecords != null) { - // Call dispose() on the obsolete update records. - for (UpdateRecord record : oldRecords.values()) { - if (!providerHasListener(record.mProvider, callingUid, receiver)) { - LocationProviderInterface p = mProvidersByName.get(record.mProvider); - if (p != null) { - p.removeListener(callingUid); - } - } - record.disposeLocked(); - } - // Accumulate providers - providers.addAll(oldRecords.keySet()); + // Record which providers were associated with this listener + HashSet<String> providers = new HashSet<String>(); + HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords; + if (oldRecords != null) { + // Call dispose() on the obsolete update records. + for (UpdateRecord record : oldRecords.values()) { + record.disposeLocked(false); } + // Accumulate providers + providers.addAll(oldRecords.keySet()); + } - // See if the providers associated with this listener have any - // other listeners; if one does, inform it of the new smallest minTime - // value; if one does not, disable location tracking for it - for (String provider : providers) { - // If provider is already disabled, don't need to do anything - if (!isAllowedBySettingsLocked(provider)) { - continue; - } + // update provider + for (String provider : providers) { + // If provider is already disabled, don't need to do anything + if (!isAllowedBySettingsLocked(provider)) { + continue; + } - boolean hasOtherListener = false; - ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider); - if (recordsForProvider != null && recordsForProvider.size() > 0) { - hasOtherListener = true; - } + applyRequirementsLocked(provider); + } + } - LocationProviderInterface p = mProvidersByName.get(provider); - if (p != null) { - if (hasOtherListener) { - long minTime = getMinTimeLocked(provider); - Slog.i(TAG, "remove " + provider + " (pid " + callingPid + - "), next minTime = " + minTime); - p.setMinTime(minTime, mTmpWorkSource); - } else { - Slog.i(TAG, "remove " + provider + " (pid " + callingPid + - "), disabled"); - p.enableLocationTracking(false); - } - } + @Override + public Location getLastLocation(LocationRequest request, String packageName) { + if (D) Log.d(TAG, "getLastLocation: " + request); + if (request == null) request = DEFAULT_LOCATION_REQUEST; + 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 (ACCESS_FINE_LOCATION.equals(perm)) { + return location; + } else { + return mLocationFudger.getOrCreate(location); } + } + } + + @Override + public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent, + String packageName) { + if (request == null) request = DEFAULT_LOCATION_REQUEST; + checkPermissionAndRequest(request); + checkPendingIntent(intent); + checkPackageName(packageName); + + if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent); + + // geo-fence manager uses the public location API, need to clear identity + int uid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + try { + mGeofenceManager.addFence(request, geofence, intent, uid, packageName); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) { + checkPermission(); + checkPendingIntent(intent); + checkPackageName(packageName); + + if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent); + + // geo-fence manager uses the public location API, need to clear identity + long identity = Binder.clearCallingIdentity(); + try { + mGeofenceManager.removeFence(geofence, intent); } finally { Binder.restoreCallingIdentity(identity); } } + + @Override public boolean addGpsStatusListener(IGpsStatusListener listener) { if (mGpsStatusProvider == null) { return false; @@ -1406,6 +1150,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return true; } + @Override public void removeGpsStatusListener(IGpsStatusListener listener) { synchronized (mLock) { try { @@ -1416,14 +1161,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public boolean sendExtraCommand(String provider, String command, Bundle extras) { if (provider == null) { // throw NullPointerException to remain compatible with previous implementation throw new NullPointerException(); } - // first check for permission to the provider - checkPermissionsSafe(provider, null); + checkPermission(); // and check for ACCESS_LOCATION_EXTRA_COMMANDS if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { @@ -1432,424 +1177,113 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (mLock) { LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) { - return false; - } - + if (p == null) return false; + return p.sendExtraCommand(command, extras); } } - public boolean sendNiResponse(int notifId, int userResponse) - { + @Override + public boolean sendNiResponse(int notifId, int userResponse) { if (Binder.getCallingUid() != Process.myUid()) { throw new SecurityException( "calling sendNiResponse from outside of the system is not allowed"); } try { return mNetInitiatedListener.sendNiResponse(notifId, userResponse); - } - catch (RemoteException e) - { + } catch (RemoteException e) { Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse"); return false; } } - class ProximityAlert { - final int mUid; - final double mLatitude; - final double mLongitude; - final float mRadius; - final long mExpiration; - final PendingIntent mIntent; - final Location mLocation; - final String mPackageName; - - public ProximityAlert(int uid, double latitude, double longitude, - float radius, long expiration, PendingIntent intent, String packageName) { - mUid = uid; - mLatitude = latitude; - mLongitude = longitude; - mRadius = radius; - mExpiration = expiration; - mIntent = intent; - mPackageName = packageName; - - mLocation = new Location(""); - mLocation.setLatitude(latitude); - mLocation.setLongitude(longitude); - } - - long getExpiration() { - return mExpiration; - } - - PendingIntent getIntent() { - return mIntent; - } - - boolean isInProximity(double latitude, double longitude, float accuracy) { - Location loc = new Location(""); - loc.setLatitude(latitude); - loc.setLongitude(longitude); - - double radius = loc.distanceTo(mLocation); - return radius <= Math.max(mRadius,accuracy); - } - - @Override - public String toString() { - return "ProximityAlert{" - + Integer.toHexString(System.identityHashCode(this)) - + " uid " + mUid + mIntent + "}"; - } - - void dump(PrintWriter pw, String prefix) { - pw.println(prefix + this); - pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude); - pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration); - pw.println(prefix + "mIntent=" + mIntent); - pw.println(prefix + "mLocation:"); - mLocation.dump(new PrintWriterPrinter(pw), prefix + " "); - } - } - - // Listener for receiving locations to trigger proximity alerts - class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished { - - boolean isGpsAvailable = false; - - // Note: this is called with the lock held. - public void onLocationChanged(Location loc) { - - // If Gps is available, then ignore updates from NetworkLocationProvider - if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) { - isGpsAvailable = true; - } - if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) { - return; - } - - // Process proximity alerts - long now = System.currentTimeMillis(); - double latitude = loc.getLatitude(); - double longitude = loc.getLongitude(); - float accuracy = loc.getAccuracy(); - ArrayList<PendingIntent> intentsToRemove = null; - - for (ProximityAlert alert : mProximityAlerts.values()) { - PendingIntent intent = alert.getIntent(); - long expiration = alert.getExpiration(); - - if (inBlacklist(alert.mPackageName)) { - continue; - } - - if ((expiration == -1) || (now <= expiration)) { - boolean entered = mProximitiesEntered.contains(alert); - boolean inProximity = - alert.isInProximity(latitude, longitude, accuracy); - if (!entered && inProximity) { - if (LOCAL_LOGV) { - Slog.v(TAG, "Entered alert"); - } - mProximitiesEntered.add(alert); - Intent enteredIntent = new Intent(); - enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true); - try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcasts() - // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, enteredIntent, this, mLocationHandler, - ACCESS_FINE_LOCATION); - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcasts(); - } - } catch (PendingIntent.CanceledException e) { - if (LOCAL_LOGV) { - Slog.v(TAG, "Canceled proximity alert: " + alert, e); - } - if (intentsToRemove == null) { - intentsToRemove = new ArrayList<PendingIntent>(); - } - intentsToRemove.add(intent); - } - } else if (entered && !inProximity) { - if (LOCAL_LOGV) { - Slog.v(TAG, "Exited alert"); - } - mProximitiesEntered.remove(alert); - Intent exitedIntent = new Intent(); - exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false); - try { - synchronized (this) { - // synchronize to ensure incrementPendingBroadcasts() - // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, exitedIntent, this, mLocationHandler, - ACCESS_FINE_LOCATION); - // call this after broadcasting so we do not increment - // if we throw an exeption. - incrementPendingBroadcasts(); - } - } catch (PendingIntent.CanceledException e) { - if (LOCAL_LOGV) { - Slog.v(TAG, "Canceled proximity alert: " + alert, e); - } - if (intentsToRemove == null) { - intentsToRemove = new ArrayList<PendingIntent>(); - } - intentsToRemove.add(intent); - } - } - } else { - // Mark alert for expiration - if (LOCAL_LOGV) { - Slog.v(TAG, "Expiring proximity alert: " + alert); - } - if (intentsToRemove == null) { - intentsToRemove = new ArrayList<PendingIntent>(); - } - intentsToRemove.add(alert.getIntent()); - } - } - - // Remove expired alerts - if (intentsToRemove != null) { - for (PendingIntent i : intentsToRemove) { - ProximityAlert alert = mProximityAlerts.get(i); - mProximitiesEntered.remove(alert); - removeProximityAlertLocked(i); - } - } - } - - // Note: this is called with the lock held. - public void onProviderDisabled(String provider) { - if (provider.equals(LocationManager.GPS_PROVIDER)) { - isGpsAvailable = false; - } - } - - // Note: this is called with the lock held. - public void onProviderEnabled(String provider) { - // ignore - } - - // Note: this is called with the lock held. - public void onStatusChanged(String provider, int status, Bundle extras) { - if ((provider.equals(LocationManager.GPS_PROVIDER)) && - (status != LocationProvider.AVAILABLE)) { - isGpsAvailable = false; - } - } - - public void onSendFinished(PendingIntent pendingIntent, Intent intent, - int resultCode, String resultData, Bundle resultExtras) { - // synchronize to ensure incrementPendingBroadcasts() - // is called before decrementPendingBroadcasts() - synchronized (this) { - decrementPendingBroadcasts(); - } - } - } - - public void addProximityAlert(double latitude, double longitude, - float radius, long expiration, PendingIntent intent, String packageName) { - validatePendingIntent(intent); - try { - synchronized (mLock) { - addProximityAlertLocked(latitude, longitude, radius, expiration, intent, - packageName); - } - } catch (SecurityException se) { - throw se; - } catch (IllegalArgumentException iae) { - throw iae; - } catch (Exception e) { - Slog.e(TAG, "addProximityAlert got exception:", e); - } - } - - private void addProximityAlertLocked(double latitude, double longitude, - float radius, long expiration, PendingIntent intent, String packageName) { - if (LOCAL_LOGV) { - Slog.v(TAG, "addProximityAlert: latitude = " + latitude + - ", longitude = " + longitude + - ", expiration = " + expiration + - ", intent = " + intent); - } - - checkPackageName(Binder.getCallingUid(), packageName); - - // Require ability to access all providers for now - if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) || - !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) { - throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); - } - - if (expiration != -1) { - expiration += System.currentTimeMillis(); - } - ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(), - latitude, longitude, radius, expiration, intent, packageName); - mProximityAlerts.put(intent, alert); - - if (mProximityReceiver == null) { - mProximityListener = new ProximityListener(); - mProximityReceiver = new Receiver(mProximityListener, packageName); - - for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderInterface provider = mProviders.get(i); - requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, - false, mProximityReceiver); - } - } - } - - public void removeProximityAlert(PendingIntent intent) { - try { - synchronized (mLock) { - removeProximityAlertLocked(intent); - } - } catch (SecurityException se) { - throw se; - } catch (IllegalArgumentException iae) { - throw iae; - } catch (Exception e) { - Slog.e(TAG, "removeProximityAlert got exception:", e); - } - } - - private void removeProximityAlertLocked(PendingIntent intent) { - if (LOCAL_LOGV) { - Slog.v(TAG, "removeProximityAlert: intent = " + intent); - } - - mProximityAlerts.remove(intent); - if (mProximityAlerts.size() == 0) { - if (mProximityReceiver != null) { - removeUpdatesLocked(mProximityReceiver); - } - mProximityReceiver = null; - mProximityListener = null; - } - } - /** * @return null if the provider does not exist * @throws SecurityException if the provider is not allowed to be * accessed by the caller */ - public Bundle getProviderInfo(String provider) { - try { - synchronized (mLock) { - return _getProviderInfoLocked(provider); - } - } catch (SecurityException se) { - throw se; - } catch (IllegalArgumentException iae) { - throw iae; - } catch (Exception e) { - Slog.e(TAG, "_getProviderInfo got exception:", e); - return null; - } - } + @Override + public ProviderProperties getProviderProperties(String provider) { + checkPermission(); - private Bundle _getProviderInfoLocked(String provider) { - LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) { - return null; + LocationProviderInterface p; + synchronized (mLock) { + p = mProvidersByName.get(provider); } - checkPermissionsSafe(provider, null); - - Bundle b = new Bundle(); - b.putBoolean("network", p.requiresNetwork()); - b.putBoolean("satellite", p.requiresSatellite()); - b.putBoolean("cell", p.requiresCell()); - b.putBoolean("cost", p.hasMonetaryCost()); - b.putBoolean("altitude", p.supportsAltitude()); - b.putBoolean("speed", p.supportsSpeed()); - b.putBoolean("bearing", p.supportsBearing()); - b.putInt("power", p.getPowerRequirement()); - b.putInt("accuracy", p.getAccuracy()); - - return b; + if (p == null) return null; + return p.getProperties(); } + @Override public boolean isProviderEnabled(String provider) { - try { - synchronized (mLock) { - return _isProviderEnabledLocked(provider); - } - } catch (SecurityException se) { - throw se; - } catch (Exception e) { - Slog.e(TAG, "isProviderEnabled got exception:", e); - return false; + checkPermission(); + if (LocationManager.FUSED_PROVIDER.equals(provider)) return false; + + synchronized (mLock) { + LocationProviderInterface p = mProvidersByName.get(provider); + if (p == null) return false; + + return isAllowedBySettingsLocked(provider); } } - public void reportLocation(Location location, boolean passive) { + private void checkCallerIsProvider() { if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission"); + == PackageManager.PERMISSION_GRANTED) { + return; } - mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location); - Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location); - m.arg1 = (passive ? 1 : 0); - mLocationHandler.sendMessageAtFrontOfQueue(m); - } + // Previously we only used the INSTALL_LOCATION_PROVIDER + // check. But that is system or signature + // protection level which is not flexible enough for + // providers installed oustide the system image. So + // also allow providers with a UID matching the + // currently bound package name - private boolean _isProviderEnabledLocked(String provider) { - checkPermissionsSafe(provider, null); + int uid = Binder.getCallingUid(); - LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) { - return false; + if (mGeocodeProvider != null) { + if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return; } - return isAllowedBySettingsLocked(provider); + for (LocationProviderProxy proxy : mProxyProviders) { + if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return; + } + throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " + + "or UID of a currently bound location provider"); } - public Location getLastKnownLocation(String provider, String packageName) { - if (LOCAL_LOGV) { - Slog.v(TAG, "getLastKnownLocation: " + provider); + private boolean doesPackageHaveUid(int uid, String packageName) { + if (packageName == null) { + return false; } try { - synchronized (mLock) { - return _getLastKnownLocationLocked(provider, packageName); + ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0); + if (appInfo.uid != uid) { + return false; } - } catch (SecurityException se) { - throw se; - } catch (Exception e) { - Slog.e(TAG, "getLastKnownLocation got exception:", e); - return null; + } catch (NameNotFoundException e) { + return false; } + return true; } - private Location _getLastKnownLocationLocked(String provider, String packageName) { - checkPermissionsSafe(provider, null); - checkPackageName(Binder.getCallingUid(), packageName); - - LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) { - return null; - } - - if (!isAllowedBySettingsLocked(provider)) { - return null; - } + @Override + public void reportLocation(Location location, boolean passive) { + checkCallerIsProvider(); - if (inBlacklist(packageName)) { - return null; + if (!location.isComplete()) { + Log.w(TAG, "Dropping incomplete location: " + location); + return; } - return mLastKnownLocation.get(provider); + mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location); + Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location); + m.arg1 = (passive ? 1 : 0); + mLocationHandler.sendMessageAtFrontOfQueue(m); } + private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) { // Always broadcast the first update if (lastLoc == null) { @@ -1857,13 +1291,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } // Check whether sufficient time has passed - long minTime = record.mMinTime; - if (loc.getTime() - lastLoc.getTime() < minTime - MAX_PROVIDER_SCHEDULING_JITTER) { + long minTime = record.mRequest.getFastestInterval(); + long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L; + if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) { return false; } // Check whether sufficient distance has been traveled - double minDistance = record.mMinDistance; + double minDistance = record.mRequest.getSmallestDisplacement(); if (minDistance > 0.0) { if (loc.distanceTo(lastLoc) <= minDistance) { return false; @@ -1874,24 +1309,26 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private void handleLocationChangedLocked(Location location, boolean passive) { + if (D) Log.d(TAG, "incoming location: " + location); + + 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; - } + if (records == null || records.size() == 0) return; LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) { - return; - } + if (p == null) return; + + // Add the coarse location as an extra + Location coarse = mLocationFudger.getOrCreate(location); - // Update last known location for provider - Location lastLocation = mLastKnownLocation.get(provider); + // Update last known locations + Location lastLocation = mLastLocation.get(provider); if (lastLocation == null) { - mLastKnownLocation.put(provider, new Location(location)); - } else { - lastLocation.set(location); + lastLocation = new Location(provider); + mLastLocation.put(provider, lastLocation); } + lastLocation.set(location); // Fetch latest status update time long newStatusUpdateTime = p.getStatusUpdateTime(); @@ -1901,18 +1338,25 @@ public class LocationManagerService extends ILocationManager.Stub implements Run int status = p.getStatus(extras); ArrayList<Receiver> deadReceivers = null; - + ArrayList<UpdateRecord> deadUpdateRecords = null; + // Broadcast location or status to all listeners - final int N = records.size(); - for (int i=0; i<N; i++) { - UpdateRecord r = records.get(i); + for (UpdateRecord r : records) { Receiver receiver = r.mReceiver; boolean receiverDead = false; - if (inBlacklist(receiver.mPackageName)) { + if (mBlacklist.isBlacklisted(receiver.mPackageName)) { + if (D) Log.d(TAG, "skipping loc update for blacklisted app: " + + receiver.mPackageName); continue; } + if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) { + location = lastLocation; // use fine location + } else { + location = coarse; // use coarse location + } + Location lastLoc = r.mLastFixBroadcast; if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) { if (lastLoc == null) { @@ -1938,8 +1382,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - // remove receiver if it is dead or we just processed a single shot request - if (receiverDead || r.mSingleShot) { + // track expired records + if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) { + if (deadUpdateRecords == null) { + deadUpdateRecords = new ArrayList<UpdateRecord>(); + } + deadUpdateRecords.add(r); + } + // track dead receivers + if (receiverDead) { if (deadReceivers == null) { deadReceivers = new ArrayList<Receiver>(); } @@ -1948,185 +1399,72 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } } - + + // remove dead records and receivers outside the loop if (deadReceivers != null) { - for (int i=deadReceivers.size()-1; i>=0; i--) { - removeUpdatesLocked(deadReceivers.get(i)); + for (Receiver receiver : deadReceivers) { + removeUpdatesLocked(receiver); + } + } + if (deadUpdateRecords != null) { + for (UpdateRecord r : deadUpdateRecords) { + r.disposeLocked(true); } } } private class LocationWorkerHandler extends Handler { - @Override public void handleMessage(Message msg) { - try { - if (msg.what == MESSAGE_LOCATION_CHANGED) { - // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!"); - - synchronized (mLock) { - Location location = (Location) msg.obj; - String provider = location.getProvider(); - boolean passive = (msg.arg1 == 1); - - if (!passive) { - // notify other providers of the new location - for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderInterface p = mProviders.get(i); - if (!provider.equals(p.getName())) { - p.updateLocation(location); - } - } - } + switch (msg.what) { + case MSG_LOCATION_CHANGED: + handleLocationChanged((Location) msg.obj, msg.arg1 == 1); + break; + } + } + } - if (isAllowedBySettingsLocked(provider)) { - handleLocationChangedLocked(location, passive); - } - } - } else if (msg.what == MESSAGE_PACKAGE_UPDATED) { - String packageName = (String) msg.obj; - - // reconnect to external providers if there is a better package - if (mNetworkLocationProviderPackageName != null && - mPackageManager.resolveService( - new Intent(LocationProviderProxy.SERVICE_ACTION) - .setPackage(packageName), 0) != null) { - // package implements service, perform full check - String bestPackage = findBestPackage( - LocationProviderProxy.SERVICE_ACTION, - mNetworkLocationProviderPackageName); - if (packageName.equals(bestPackage)) { - mNetworkLocationProvider.reconnect(bestPackage); - mNetworkLocationProviderPackageName = packageName; - } - } - if (mGeocodeProviderPackageName != null && - mPackageManager.resolveService( - new Intent(GeocoderProxy.SERVICE_ACTION) - .setPackage(packageName), 0) != null) { - // package implements service, perform full check - String bestPackage = findBestPackage( - GeocoderProxy.SERVICE_ACTION, - mGeocodeProviderPackageName); - if (packageName.equals(bestPackage)) { - mGeocodeProvider.reconnect(bestPackage); - mGeocodeProviderPackageName = packageName; - } - } - } - } catch (Exception e) { - // Log, don't crash! - Slog.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e); + private void handleLocationChanged(Location location, boolean passive) { + String provider = location.getProvider(); + + if (!passive) { + // notify passive provider of the new location + mPassiveProvider.updateLocation(location); + } + + synchronized (mLock) { + if (isAllowedBySettingsLocked(provider)) { + handleLocationChangedLocked(location, passive); } } } - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART); - if (queryRestart - || action.equals(Intent.ACTION_PACKAGE_REMOVED) - || action.equals(Intent.ACTION_PACKAGE_RESTARTED) - || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { - synchronized (mLock) { - int uidList[] = null; - if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { - uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); - } else { - uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)}; - } - if (uidList == null || uidList.length == 0) { - return; - } - for (int uid : uidList) { - if (uid >= 0) { - ArrayList<Receiver> removedRecs = null; - for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) { - for (int j=i.size()-1; j>=0; j--) { - UpdateRecord ur = i.get(j); - if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) { - if (queryRestart) { - setResultCode(Activity.RESULT_OK); - return; - } - if (removedRecs == null) { - removedRecs = new ArrayList<Receiver>(); - } - if (!removedRecs.contains(ur.mReceiver)) { - removedRecs.add(ur.mReceiver); - } - } - } - } - ArrayList<ProximityAlert> removedAlerts = null; - for (ProximityAlert i : mProximityAlerts.values()) { - if (i.mUid == uid) { - if (queryRestart) { - setResultCode(Activity.RESULT_OK); - return; - } - if (removedAlerts == null) { - removedAlerts = new ArrayList<ProximityAlert>(); - } - if (!removedAlerts.contains(i)) { - removedAlerts.add(i); - } - } - } - if (removedRecs != null) { - for (int i=removedRecs.size()-1; i>=0; i--) { - removeUpdatesLocked(removedRecs.get(i)); - } - } - if (removedAlerts != null) { - for (int i=removedAlerts.size()-1; i>=0; i--) { - removeProximityAlertLocked(removedAlerts.get(i).mIntent); - } - } + public void onPackageDisappeared(String packageName, int reason) { + // remove all receivers associated with this package name + synchronized (mLock) { + ArrayList<Receiver> deadReceivers = null; + + for (Receiver receiver : mReceivers.values()) { + if (receiver.mPackageName.equals(packageName)) { + if (deadReceivers == null) { + deadReceivers = new ArrayList<Receiver>(); } + deadReceivers.add(receiver); } } - } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - boolean noConnectivity = - intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); - if (!noConnectivity) { - mNetworkState = LocationProvider.AVAILABLE; - } else { - mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; - } - final ConnectivityManager connManager = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo info = connManager.getActiveNetworkInfo(); - - // Notify location providers of current network state - synchronized (mLock) { - for (int i = mProviders.size() - 1; i >= 0; i--) { - LocationProviderInterface provider = mProviders.get(i); - if (provider.requiresNetwork()) { - provider.updateNetworkState(mNetworkState, info); - } + // perform removal outside of mReceivers loop + if (deadReceivers != null) { + for (Receiver receiver : deadReceivers) { + removeUpdatesLocked(receiver); } } } } }; - private final PackageMonitor mPackageMonitor = new PackageMonitor() { - @Override - public void onPackageUpdateFinished(String packageName, int uid) { - // Called by main thread; divert work to LocationWorker. - Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget(); - } - @Override - public void onPackageAdded(String packageName, int uid) { - // Called by main thread; divert work to LocationWorker. - Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget(); - } - }; - // Wake locks private void incrementPendingBroadcasts() { @@ -2166,10 +1504,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Geocoder + @Override public boolean geocoderIsPresent() { return mGeocodeProvider != null; } + @Override public String getFromLocation(double latitude, double longitude, int maxResults, GeocoderParams params, List<Address> addrs) { if (mGeocodeProvider != null) { @@ -2180,6 +1520,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } + @Override public String getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, @@ -2205,12 +1546,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + } } - public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, - boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, - boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { + @Override + public void addTestProvider(String name, ProviderProperties properties) { checkMockPermissionsSafe(); if (LocationManager.PASSIVE_PROVIDER.equals(name)) { @@ -2219,30 +1559,28 @@ public class LocationManagerService extends ILocationManager.Stub implements Run long identity = Binder.clearCallingIdentity(); synchronized (mLock) { - MockProvider provider = new MockProvider(name, this, - requiresNetwork, requiresSatellite, - requiresCell, hasMonetaryCost, supportsAltitude, - supportsSpeed, supportsBearing, powerRequirement, accuracy); + MockProvider provider = new MockProvider(name, this, properties); // remove the real provider if we are replacing GPS or network provider if (LocationManager.GPS_PROVIDER.equals(name) - || LocationManager.NETWORK_PROVIDER.equals(name)) { + || LocationManager.NETWORK_PROVIDER.equals(name) + || LocationManager.FUSED_PROVIDER.equals(name)) { LocationProviderInterface p = mProvidersByName.get(name); if (p != null) { - p.enableLocationTracking(false); - removeProvider(p); + removeProviderLocked(p); } } if (mProvidersByName.get(name) != null) { throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); } - addProvider(provider); + addProviderLocked(provider); mMockProviders.put(name, provider); - mLastKnownLocation.put(name, null); + mLastLocation.put(name, null); updateProvidersLocked(); } Binder.restoreCallingIdentity(identity); } + @Override public void removeTestProvider(String provider) { checkMockPermissionsSafe(); synchronized (mLock) { @@ -2251,22 +1589,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } long identity = Binder.clearCallingIdentity(); - removeProvider(mProvidersByName.get(provider)); + removeProviderLocked(mProvidersByName.get(provider)); mMockProviders.remove(mockProvider); - // reinstall real provider if we were mocking GPS or network provider - if (LocationManager.GPS_PROVIDER.equals(provider) && - mGpsLocationProvider != null) { - addProvider(mGpsLocationProvider); - } else if (LocationManager.NETWORK_PROVIDER.equals(provider) && - mNetworkLocationProvider != null) { - addProvider(mNetworkLocationProvider); - } - mLastKnownLocation.put(provider, null); + + // reinstate real provider if available + LocationProviderInterface realProvider = mRealProviders.get(provider); + if (realProvider != null) { + addProviderLocked(realProvider); + } + mLastLocation.put(provider, null); updateProvidersLocked(); Binder.restoreCallingIdentity(identity); } } + @Override public void setTestProviderLocation(String provider, Location loc) { checkMockPermissionsSafe(); synchronized (mLock) { @@ -2281,6 +1618,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public void clearTestProviderLocation(String provider) { checkMockPermissionsSafe(); synchronized (mLock) { @@ -2292,6 +1630,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public void setTestProviderEnabled(String provider, boolean enabled) { checkMockPermissionsSafe(); synchronized (mLock) { @@ -2314,6 +1653,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public void clearTestProviderEnabled(String provider) { checkMockPermissionsSafe(); synchronized (mLock) { @@ -2329,6 +1669,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) { checkMockPermissionsSafe(); synchronized (mLock) { @@ -2340,6 +1681,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + @Override public void clearTestProviderStatus(String provider) { checkMockPermissionsSafe(); synchronized (mLock) { @@ -2351,119 +1693,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - public class BlacklistObserver extends ContentObserver { - public BlacklistObserver(Handler handler) { - super(handler); - } - @Override - public void onChange(boolean selfChange) { - reloadBlacklist(); - } - } - - private void loadBlacklist() { - // Register for changes - BlacklistObserver observer = new BlacklistObserver(mLocationHandler); - mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( - BLACKLIST_CONFIG_NAME), false, observer); - mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( - WHITELIST_CONFIG_NAME), false, observer); - reloadBlacklist(); - } - - private void reloadBlacklist() { - String blacklist[] = getStringArray(BLACKLIST_CONFIG_NAME); - String whitelist[] = getStringArray(WHITELIST_CONFIG_NAME); - synchronized (mLock) { - mWhitelist = whitelist; - Slog.i(TAG, "whitelist: " + arrayToString(mWhitelist)); - mBlacklist = blacklist; - Slog.i(TAG, "blacklist: " + arrayToString(mBlacklist)); - } - } - - private static String arrayToString(String[] array) { - StringBuilder s = new StringBuilder(); - s.append('['); - boolean first = true; - for (String a : array) { - if (!first) s.append(','); - first = false; - s.append(a); - } - s.append(']'); - return s.toString(); - } - - private String[] getStringArray(String key) { - String flatString = Settings.Secure.getString(mContext.getContentResolver(), key); - if (flatString == null) { - return new String[0]; - } - String[] splitStrings = flatString.split(","); - ArrayList<String> result = new ArrayList<String>(); - for (String pkg : splitStrings) { - pkg = pkg.trim(); - if (pkg.isEmpty()) { - continue; - } - result.add(pkg); - } - return result.toArray(new String[result.size()]); - } - - /** - * Return true if in blacklist, and not in whitelist. - */ - private boolean inBlacklist(String packageName) { - synchronized (mLock) { - for (String black : mBlacklist) { - if (packageName.startsWith(black)) { - if (inWhitelist(packageName)) { - continue; - } else { - if (LOCAL_LOGV) Log.d(TAG, "dropping location (blacklisted): " - + packageName + " matches " + black); - return true; - } - } - } - } - return false; - } - - /** - * Return true if any of packages are in whitelist - */ - private boolean inWhitelist(String pkg) { - synchronized (mLock) { - for (String white : mWhitelist) { - if (pkg.startsWith(white)) return true; - } - } - return false; - } - - private void checkPackageName(int uid, String packageName) { - if (packageName == null) { - throw new SecurityException("packageName cannot be null"); - } - String[] packages = mPackageManager.getPackagesForUid(uid); - if (packages == null) { - throw new SecurityException("invalid UID " + uid); - } - for (String pkg : packages) { - if (packageName.equals(pkg)) return; - } - throw new SecurityException("invalid package name"); - } - private void log(String log) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.d(TAG, log); } } - + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -2472,83 +1708,65 @@ public class LocationManagerService extends ILocationManager.Stub implements Run + ", uid=" + Binder.getCallingUid()); return; } - + synchronized (mLock) { pw.println("Current Location Manager state:"); - pw.println(" sProvidersLoaded=" + sProvidersLoaded); - pw.println(" Listeners:"); - int N = mReceivers.size(); - for (int i=0; i<N; i++) { - pw.println(" " + mReceivers.get(i)); - } pw.println(" Location Listeners:"); - for (Receiver i : mReceivers.values()) { - pw.println(" " + i + ":"); - for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) { - pw.println(" " + j.getKey() + ":"); - j.getValue().dump(pw, " "); - } + for (Receiver receiver : mReceivers.values()) { + pw.println(" " + receiver); } - pw.println(" Package blacklist:" + arrayToString(mBlacklist)); - pw.println(" Package whitelist:" + arrayToString(mWhitelist)); pw.println(" Records by Provider:"); - for (Map.Entry<String, ArrayList<UpdateRecord>> i - : mRecordsByProvider.entrySet()) { - pw.println(" " + i.getKey() + ":"); - for (UpdateRecord j : i.getValue()) { - pw.println(" " + j + ":"); - j.dump(pw, " "); + for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) { + pw.println(" " + entry.getKey() + ":"); + for (UpdateRecord record : entry.getValue()) { + pw.println(" " + record); } } pw.println(" Last Known Locations:"); - for (Map.Entry<String, Location> i - : mLastKnownLocation.entrySet()) { - pw.println(" " + i.getKey() + ":"); - i.getValue().dump(new PrintWriterPrinter(pw), " "); - } - if (mProximityAlerts.size() > 0) { - pw.println(" Proximity Alerts:"); - for (Map.Entry<PendingIntent, ProximityAlert> i - : mProximityAlerts.entrySet()) { - pw.println(" " + i.getKey() + ":"); - i.getValue().dump(pw, " "); - } - } - if (mProximitiesEntered.size() > 0) { - pw.println(" Proximities Entered:"); - for (ProximityAlert i : mProximitiesEntered) { - pw.println(" " + i + ":"); - i.dump(pw, " "); - } + for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) { + String provider = entry.getKey(); + Location location = entry.getValue(); + pw.println(" " + provider + ": " + location); } - pw.println(" mProximityReceiver=" + mProximityReceiver); - pw.println(" mProximityListener=" + mProximityListener); + + mGeofenceManager.dump(pw); + if (mEnabledProviders.size() > 0) { pw.println(" Enabled Providers:"); for (String i : mEnabledProviders) { pw.println(" " + i); } - + } if (mDisabledProviders.size() > 0) { pw.println(" Disabled Providers:"); for (String i : mDisabledProviders) { pw.println(" " + i); } - } + pw.append(" "); + mBlacklist.dump(pw); if (mMockProviders.size() > 0) { pw.println(" Mock Providers:"); for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) { i.getValue().dump(pw, " "); } } + + pw.append(" fudger: "); + mLocationFudger.dump(fd, pw, args); + + if (args.length > 0 && "short".equals(args[0])) { + return; + } for (LocationProviderInterface provider: mProviders) { - String state = provider.getInternalState(); - if (state != null) { - pw.println(provider.getName() + " Internal State:"); - pw.write(state); + pw.print(provider.getName() + " Internal State"); + if (provider instanceof LocationProviderProxy) { + LocationProviderProxy proxy = (LocationProviderProxy) provider; + pw.print(" (" + proxy.getConnectedPackageName() + ")"); } + pw.println(":"); + provider.dump(fd, pw, args); } } } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 04267a3..bb5d552 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -48,7 +48,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UserId; +import android.os.UserHandle; import android.os.storage.IMountService; import android.os.storage.IMountServiceListener; import android.os.storage.IMountShutdownObserver; @@ -1713,7 +1713,7 @@ class MountService extends IMountService.Stub return false; } - final int packageUid = mPms.getPackageUid(packageName, UserId.getUserId(callerUid)); + final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid)); if (DEBUG_OBB) { Slog.d(TAG, "packageName = " + packageName + ", packageUid = " + diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java index f71125a..92af9a9 100644 --- a/services/java/com/android/server/NativeDaemonConnector.java +++ b/services/java/com/android/server/NativeDaemonConnector.java @@ -35,6 +35,9 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.LinkedList; /** @@ -482,102 +485,108 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo private static class ResponseQueue { - private static class Response { + private static class PendingCmd { public int cmdNum; - public LinkedList<NativeDaemonEvent> responses = new LinkedList<NativeDaemonEvent>(); + public BlockingQueue<NativeDaemonEvent> responses = + new ArrayBlockingQueue<NativeDaemonEvent>(10); public String request; - public Response(int c, String r) {cmdNum = c; request = r;} + + // The availableResponseCount member is used to track when we can remove this + // instance from the ResponseQueue. + // This is used under the protection of a sync of the mPendingCmds object. + // A positive value means we've had more writers retreive this object while + // a negative value means we've had more readers. When we've had an equal number + // (it goes to zero) we can remove this object from the mPendingCmds list. + // Note that we may have more responses for this command (and more readers + // coming), but that would result in a new PendingCmd instance being created + // and added with the same cmdNum. + // Also note that when this goes to zero it just means a parity of readers and + // writers have retrieved this object - not that they are done using it. The + // responses queue may well have more responses yet to be read or may get more + // responses added to it. But all those readers/writers have retreived and + // hold references to this instance already so it can be removed from + // mPendingCmds queue. + public int availableResponseCount; + public PendingCmd(int c, String r) {cmdNum = c; request = r;} } - private final LinkedList<Response> mResponses; + private final LinkedList<PendingCmd> mPendingCmds; private int mMaxCount; ResponseQueue(int maxCount) { - mResponses = new LinkedList<Response>(); + mPendingCmds = new LinkedList<PendingCmd>(); mMaxCount = maxCount; } public void add(int cmdNum, NativeDaemonEvent response) { - Response found = null; - synchronized (mResponses) { - for (Response r : mResponses) { - if (r.cmdNum == cmdNum) { - found = r; + PendingCmd found = null; + synchronized (mPendingCmds) { + for (PendingCmd pendingCmd : mPendingCmds) { + if (pendingCmd.cmdNum == cmdNum) { + found = pendingCmd; break; } } if (found == null) { // didn't find it - make sure our queue isn't too big before adding - // another.. - while (mResponses.size() >= mMaxCount) { + while (mPendingCmds.size() >= mMaxCount) { Slog.e("NativeDaemonConnector.ResponseQueue", - "more buffered than allowed: " + mResponses.size() + + "more buffered than allowed: " + mPendingCmds.size() + " >= " + mMaxCount); // let any waiter timeout waiting for this - Response r = mResponses.remove(); + PendingCmd pendingCmd = mPendingCmds.remove(); Slog.e("NativeDaemonConnector.ResponseQueue", - "Removing request: " + r.request + " (" + r.cmdNum + ")"); + "Removing request: " + pendingCmd.request + " (" + + pendingCmd.cmdNum + ")"); } - found = new Response(cmdNum, null); - mResponses.add(found); + found = new PendingCmd(cmdNum, null); + mPendingCmds.add(found); } - found.responses.add(response); - } - synchronized (found) { - found.notify(); + found.availableResponseCount++; + // if a matching remove call has already retrieved this we can remove this + // instance from our list + if (found.availableResponseCount == 0) mPendingCmds.remove(found); } + try { + found.responses.put(response); + } catch (InterruptedException e) { } } // note that the timeout does not count time in deep sleep. If you don't want // the device to sleep, hold a wakelock public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) { - long endTime = SystemClock.uptimeMillis() + timeoutMs; - long nowTime; - Response found = null; - while (true) { - synchronized (mResponses) { - for (Response response : mResponses) { - if (response.cmdNum == cmdNum) { - found = response; - // how many response fragments are left - switch (response.responses.size()) { - case 0: // haven't got any - must wait - break; - case 1: // last one - remove this from the master list - mResponses.remove(response); // fall through - default: // take one and move on - response.request = origCmd; - return response.responses.remove(); - } - } - } - nowTime = SystemClock.uptimeMillis(); - if (endTime <= nowTime) { - Slog.e("NativeDaemonConnector.ResponseQueue", - "Timeout waiting for response"); - return null; - } - /* pre-allocate so we have something unique to wait on */ - if (found == null) { - found = new Response(cmdNum, origCmd); - mResponses.add(found); + PendingCmd found = null; + synchronized (mPendingCmds) { + for (PendingCmd pendingCmd : mPendingCmds) { + if (pendingCmd.cmdNum == cmdNum) { + found = pendingCmd; + break; } } - try { - synchronized (found) { - found.wait(endTime - nowTime); - } - } catch (InterruptedException e) { - // loop around to check if we're done or if it's time to stop waiting + if (found == null) { + found = new PendingCmd(cmdNum, origCmd); + mPendingCmds.add(found); } + found.availableResponseCount--; + // if a matching add call has already retrieved this we can remove this + // instance from our list + if (found.availableResponseCount == 0) mPendingCmds.remove(found); + } + NativeDaemonEvent result = null; + try { + result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) {} + if (result == null) { + Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response"); } + return result; } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Pending requests:"); - synchronized (mResponses) { - for (Response response : mResponses) { - pw.println(" Cmd " + response.cmdNum + " - " + response.request); + synchronized (mPendingCmds) { + for (PendingCmd pendingCmd : mPendingCmds) { + pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.request); } } } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 11644e3..efa16af 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -35,6 +35,7 @@ import static com.android.server.NetworkManagementService.NetdResponseCode.Tethe import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult; import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; +import android.bluetooth.BluetoothTetheringDataTracker; import android.content.Context; import android.net.INetworkManagementEventObserver; import android.net.InterfaceConfiguration; @@ -55,7 +56,9 @@ import android.util.Slog; import android.util.SparseBooleanArray; import com.android.internal.net.NetworkStatsFactory; +import com.android.internal.util.Preconditions; import com.android.server.NativeDaemonConnector.Command; +import com.android.server.net.LockdownVpnTracker; import com.google.android.collect.Maps; import java.io.BufferedReader; @@ -91,6 +94,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub private static final String ADD = "add"; private static final String REMOVE = "remove"; + private static final String ALLOW = "allow"; + private static final String DENY = "deny"; + private static final String DEFAULT = "default"; private static final String SECONDARY = "secondary"; @@ -121,6 +127,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub public static final int InterfaceChange = 600; public static final int BandwidthControl = 601; + public static final int InterfaceClassActivity = 613; } /** @@ -151,7 +158,23 @@ public class NetworkManagementService extends INetworkManagementService.Stub /** Set of UIDs with active reject rules. */ private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray(); + private Object mIdleTimerLock = new Object(); + /** Set of interfaces with active idle timers. */ + private static class IdleTimerParams { + public final int timeout; + public final String label; + public int networkCount; + + IdleTimerParams(int timeout, String label) { + this.timeout = timeout; + this.label = label; + this.networkCount = 1; + } + } + private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap(); + private volatile boolean mBandwidthControlEnabled; + private volatile boolean mFirewallEnabled; /** * Constructs a new NetworkManagementService instance @@ -278,6 +301,20 @@ public class NetworkManagementService extends INetworkManagementService.Stub } /** + * Notify our observers of a change in the data activity state of the interface + */ + private void notifyInterfaceClassActivity(String label, boolean active) { + final int length = mObservers.beginBroadcast(); + for (int i = 0; i < length; i++) { + try { + mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active); + } catch (RemoteException e) { + } + } + mObservers.finishBroadcast(); + } + + /** * Prepare native daemon once connected, enabling modules and pushing any * existing in-memory rules. */ @@ -332,6 +369,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } } + + // TODO: Push any existing firewall state + setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled()); } // @@ -403,6 +443,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException( String.format("Invalid event from daemon (%s)", raw)); // break; + case NetdResponseCode.InterfaceClassActivity: + /* + * An network interface class state changed (active/idle) + * Format: "NNN IfaceClass <active/idle> <label>" + */ + if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) { + throw new IllegalStateException( + String.format("Invalid event from daemon (%s)", raw)); + } + boolean isActive = cooked[2].equals("active"); + notifyInterfaceClassActivity(cooked[3], isActive); + return true; + // break; default: break; } return false; @@ -780,6 +833,36 @@ public class NetworkManagementService extends INetworkManagementService.Stub return event.getMessage().endsWith("started"); } + // TODO(BT) Remove + public void startReverseTethering(String iface) + throws IllegalStateException { + if (DBG) Slog.d(TAG, "startReverseTethering in"); + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + // cmd is "tether start first_start first_stop second_start second_stop ..." + // an odd number of addrs will fail + String cmd = "tether start-reverse"; + cmd += " " + iface; + if (DBG) Slog.d(TAG, "startReverseTethering cmd: " + cmd); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Unable to communicate to native daemon"); + } + BluetoothTetheringDataTracker.getInstance().startReverseTether(iface); + + } + + // TODO(BT) Remove + public void stopReverseTethering() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + try { + mConnector.doCommand("tether stop-reverse"); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Unable to communicate to native daemon to stop tether"); + } + BluetoothTetheringDataTracker.getInstance().stopReverseTether(); + } + @Override public void tetherInterface(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); @@ -923,14 +1006,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void startAccessPoint( - WifiConfiguration wifiConfig, String wlanIface, String softapIface) { + WifiConfiguration wifiConfig, String wlanIface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { wifiFirmwareReload(wlanIface, "AP"); if (wifiConfig == null) { - mConnector.execute("softap", "set", wlanIface, softapIface); + mConnector.execute("softap", "set", wlanIface); } else { - mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID, + mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, getSecurityType(wifiConfig), wifiConfig.preSharedKey); } mConnector.execute("softap", "startap"); @@ -966,7 +1049,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { mConnector.execute("softap", "stopap"); - mConnector.execute("softap", "stop", wlanIface); wifiFirmwareReload(wlanIface, "STA"); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); @@ -974,13 +1056,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) { + public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { if (wifiConfig == null) { - mConnector.execute("softap", "set", wlanIface, softapIface); + mConnector.execute("softap", "set", wlanIface); } else { - mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID, + mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, getSecurityType(wifiConfig), wifiConfig.preSharedKey); } } catch (NativeDaemonConnectorException e) { @@ -989,6 +1071,51 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override + public void addIdleTimer(String iface, int timeout, String label) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + if (DBG) Slog.d(TAG, "Adding idletimer"); + + synchronized (mIdleTimerLock) { + IdleTimerParams params = mActiveIdleTimers.get(iface); + if (params != null) { + // the interface already has idletimer, update network count + params.networkCount++; + return; + } + + try { + mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), label); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, label)); + } + } + + @Override + public void removeIdleTimer(String iface) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + if (DBG) Slog.d(TAG, "Removing idletimer"); + + synchronized (mIdleTimerLock) { + IdleTimerParams params = mActiveIdleTimers.get(iface); + if (params == null || --(params.networkCount) > 0) { + return; + } + + try { + mConnector.execute("idletimer", "remove", iface, + Integer.toString(params.timeout), params.label); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + mActiveIdleTimers.remove(iface); + } + } + + @Override public NetworkStats getNetworkStatsSummaryDev() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); return mStatsFactory.readNetworkStatsSummaryDev(); @@ -1307,7 +1434,72 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - /** {@inheritDoc} */ + @Override + public void setFirewallEnabled(boolean enabled) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + try { + mConnector.execute("firewall", enabled ? "enable" : "disable"); + mFirewallEnabled = enabled; + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public boolean isFirewallEnabled() { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + return mFirewallEnabled; + } + + @Override + public void setFirewallInterfaceRule(String iface, boolean allow) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + Preconditions.checkState(mFirewallEnabled); + final String rule = allow ? ALLOW : DENY; + try { + mConnector.execute("firewall", "set_interface_rule", iface, rule); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void setFirewallEgressSourceRule(String addr, boolean allow) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + Preconditions.checkState(mFirewallEnabled); + final String rule = allow ? ALLOW : DENY; + try { + mConnector.execute("firewall", "set_egress_source_rule", addr, rule); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void setFirewallEgressDestRule(String addr, int port, boolean allow) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + Preconditions.checkState(mFirewallEnabled); + final String rule = allow ? ALLOW : DENY; + try { + mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void setFirewallUidRule(int uid, boolean allow) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + Preconditions.checkState(mFirewallEnabled); + final String rule = allow ? ALLOW : DENY; + try { + mConnector.execute("firewall", "set_uid_rule", uid, rule); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override public void monitor() { if (mConnector != null) { mConnector.monitor(); @@ -1338,5 +1530,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub } pw.println("]"); } + + pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index f6d3b608..d6fed39 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -48,11 +48,13 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserId; +import android.os.UserHandle; import android.os.Vibrator; import android.provider.Settings; +import android.service.dreams.IDreamManager; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.AtomicFile; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -61,7 +63,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; -import com.android.internal.os.AtomicFile; import com.android.internal.statusbar.StatusBarNotification; import com.android.internal.util.FastXmlSerializer; @@ -148,6 +149,8 @@ public class NotificationManagerService extends INotificationManager.Stub private AtomicFile mPolicyFile; private HashSet<String> mBlockedPackages = new HashSet<String>(); + private IDreamManager mSandman; + private static final int DB_VERSION = 1; private static final String TAG_BODY = "notification-policy"; @@ -634,6 +637,8 @@ public class NotificationManagerService extends INotificationManager.Stub void systemReady() { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); + mSandman = IDreamManager.Stub.asInterface( + ServiceManager.getService("dreams")); // no beeping until we're basically done booting mSystemReady = true; @@ -972,6 +977,16 @@ public class NotificationManagerService extends INotificationManager.Stub | Notification.FLAG_NO_CLEAR; } + // Stop screensaver if the notification has a full-screen intent. + // (like an incoming phone call) + if (notification.fullScreenIntent != null && mSandman != null) { + try { + mSandman.awaken(); + } catch (RemoteException e) { + // noop + } + } + if (notification.icon != 0) { StatusBarNotification n = new StatusBarNotification(pkg, id, tag, r.uid, r.initialPid, score, notification); @@ -1257,7 +1272,7 @@ public class NotificationManagerService extends INotificationManager.Stub void checkCallerIsSystem() { int uid = Binder.getCallingUid(); - if (uid == Process.SYSTEM_UID || uid == 0) { + if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { return; } throw new SecurityException("Disallowed call for uid " + uid); @@ -1265,13 +1280,13 @@ public class NotificationManagerService extends INotificationManager.Stub void checkCallerIsSystemOrSameApp(String pkg) { int uid = Binder.getCallingUid(); - if (uid == Process.SYSTEM_UID || uid == 0) { + if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { return; } try { ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( pkg, 0); - if (!UserId.isSameApp(ai.uid, uid)) { + if (!UserHandle.isSameApp(ai.uid, uid)) { throw new SecurityException("Calling uid " + uid + " gave package" + pkg + " which is owned by uid " + ai.uid); } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java deleted file mode 100644 index 888ec69..0000000 --- a/services/java/com/android/server/PowerManagerService.java +++ /dev/null @@ -1,3405 +0,0 @@ -/* - * Copyright (C) 2007 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; - -import com.android.internal.app.IBatteryStats; -import com.android.server.am.BatteryStatsService; -import com.android.server.pm.ShutdownThread; - -import android.app.ActivityManagerNative; -import android.app.IActivityManager; -import android.content.BroadcastReceiver; -import android.content.ContentQueryMap; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.database.Cursor; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.hardware.SystemSensorManager; -import android.os.BatteryManager; -import android.os.BatteryStats; -import android.os.Binder; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.IPowerManager; -import android.os.LocalPowerManager; -import android.os.Message; -import android.os.PowerManager; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.WorkSource; -import android.provider.Settings; -import android.util.EventLog; -import android.util.Log; -import android.util.Slog; -import android.view.WindowManagerPolicy; -import static android.view.WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR; -import static android.provider.Settings.System.DIM_SCREEN; -import static android.provider.Settings.System.SCREEN_BRIGHTNESS; -import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE; -import static android.provider.Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ; -import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; -import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; -import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN; -import static android.provider.Settings.System.WINDOW_ANIMATION_SCALE; -import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Observable; -import java.util.Observer; - -public class PowerManagerService extends IPowerManager.Stub - implements LocalPowerManager, Watchdog.Monitor { - private static final int NOMINAL_FRAME_TIME_MS = 1000/60; - - private static final String TAG = "PowerManagerService"; - static final String PARTIAL_NAME = "PowerManagerService"; - - // Wake lock that ensures that the CPU is running. The screen might not be on. - private static final int PARTIAL_WAKE_LOCK_ID = 1; - - // Wake lock that ensures that the screen is on. - private static final int FULL_WAKE_LOCK_ID = 2; - - static final boolean DEBUG_SCREEN_ON = false; - - private static final boolean LOG_PARTIAL_WL = false; - - // Indicates whether touch-down cycles should be logged as part of the - // LOG_POWER_SCREEN_STATE log events - private static final boolean LOG_TOUCH_DOWNS = true; - - private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK - | PowerManager.SCREEN_DIM_WAKE_LOCK - | PowerManager.SCREEN_BRIGHT_WAKE_LOCK - | PowerManager.FULL_WAKE_LOCK - | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK; - - // time since last state: time since last event: - // The short keylight delay comes from secure settings; this is the default. - private static final int SHORT_KEYLIGHT_DELAY_DEFAULT = 6000; // t+6 sec - private static final int MEDIUM_KEYLIGHT_DELAY = 15000; // t+15 sec - private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec - private static final int LONG_DIM_TIME = 7000; // t+N-5 sec - - // How long to wait to debounce light sensor changes in milliseconds - private static final int LIGHT_SENSOR_DELAY = 2000; - - // light sensor events rate in microseconds - private static final int LIGHT_SENSOR_RATE = 1000000; - - // Expansion of range of light values when applying scale from light - // sensor brightness setting, in the [0..255] brightness range. - private static final int LIGHT_SENSOR_RANGE_EXPANSION = 20; - - // Scaling factor of the light sensor brightness setting when applying - // it to the final brightness. - private static final int LIGHT_SENSOR_OFFSET_SCALE = 8; - - // For debouncing the proximity sensor in milliseconds - private static final int PROXIMITY_SENSOR_DELAY = 1000; - - // trigger proximity if distance is less than 5 cm - private static final float PROXIMITY_THRESHOLD = 5.0f; - - // Cached secure settings; see updateSettingsValues() - private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT; - - // Default timeout for screen off, if not found in settings database = 15 seconds. - private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000; - - // Screen brightness should always have a value, but just in case... - private static final int DEFAULT_SCREEN_BRIGHTNESS = 192; - - // Threshold for BRIGHTNESS_LOW_BATTERY (percentage) - // Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD - private static final int LOW_BATTERY_THRESHOLD = 10; - - // flags for setPowerState - private static final int ALL_LIGHTS_OFF = 0x00000000; - private static final int SCREEN_ON_BIT = 0x00000001; - private static final int SCREEN_BRIGHT_BIT = 0x00000002; - private static final int BUTTON_BRIGHT_BIT = 0x00000004; - private static final int KEYBOARD_BRIGHT_BIT = 0x00000008; - private static final int BATTERY_LOW_BIT = 0x00000010; - - // values for setPowerState - - // SCREEN_OFF == everything off - private static final int SCREEN_OFF = 0x00000000; - - // SCREEN_DIM == screen on, screen backlight dim - private static final int SCREEN_DIM = SCREEN_ON_BIT; - - // SCREEN_BRIGHT == screen on, screen backlight bright - private static final int SCREEN_BRIGHT = SCREEN_ON_BIT | SCREEN_BRIGHT_BIT; - - // SCREEN_BUTTON_BRIGHT == screen on, screen and button backlights bright - private static final int SCREEN_BUTTON_BRIGHT = SCREEN_BRIGHT | BUTTON_BRIGHT_BIT; - - // SCREEN_BUTTON_BRIGHT == screen on, screen, button and keyboard backlights bright - private static final int ALL_BRIGHT = SCREEN_BUTTON_BRIGHT | KEYBOARD_BRIGHT_BIT; - - // used for noChangeLights in setPowerState() - private static final int LIGHTS_MASK = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT; - - // animate screen lights in PowerManager (as opposed to SurfaceFlinger) - boolean mAnimateScreenLights = true; - - static final int ANIM_STEPS = 60; // nominal # of frames at 60Hz - // Slower animation for autobrightness changes - static final int AUTOBRIGHTNESS_ANIM_STEPS = 2 * ANIM_STEPS; - // Even slower animation for autodimness changes. Set to max to effectively disable dimming. - // Note 100 is used to keep the mWindowScaleAnimation scaling below from overflowing an int. - static final int AUTODIMNESS_ANIM_STEPS = Integer.MAX_VALUE / (NOMINAL_FRAME_TIME_MS * 100); - // Number of steps when performing a more immediate brightness change. - static final int IMMEDIATE_ANIM_STEPS = 4; - - // These magic numbers are the initial state of the LEDs at boot. Ideally - // we should read them from the driver, but our current hardware returns 0 - // for the initial value. Oops! - static final int INITIAL_SCREEN_BRIGHTNESS = 255; - static final int INITIAL_BUTTON_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF; - static final int INITIAL_KEYBOARD_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF; - - private final int MY_UID; - private final int MY_PID; - - private boolean mDoneBooting = false; - private boolean mBootCompleted = false; - private boolean mHeadless = false; - private int mStayOnConditions = 0; - private final int[] mBroadcastQueue = new int[] { -1, -1, -1 }; - private final int[] mBroadcastWhy = new int[3]; - private boolean mPreparingForScreenOn = false; - private boolean mSkippedScreenOn = false; - private boolean mInitialized = false; - private int mPartialCount = 0; - private int mPowerState; - // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER, - // WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT or WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR - private int mScreenOffReason; - private int mUserState; - private boolean mKeyboardVisible = false; - private boolean mUserActivityAllowed = true; - private int mProximityWakeLockCount = 0; - private boolean mProximitySensorEnabled = false; - private boolean mProximitySensorActive = false; - private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active - private long mLastProximityEventTime; - private int mScreenOffTimeoutSetting; - private int mMaximumScreenOffTimeout = Integer.MAX_VALUE; - private int mKeylightDelay; - private int mDimDelay; - private int mScreenOffDelay; - private int mWakeLockState; - private long mLastEventTime = 0; - private long mScreenOffTime; - private volatile WindowManagerPolicy mPolicy; - private final LockList mLocks = new LockList(); - private Intent mScreenOffIntent; - private Intent mScreenOnIntent; - private LightsService mLightsService; - private Context mContext; - private LightsService.Light mLcdLight; - private LightsService.Light mButtonLight; - private LightsService.Light mKeyboardLight; - private LightsService.Light mAttentionLight; - private UnsynchronizedWakeLock mBroadcastWakeLock; - private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock; - private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock; - private UnsynchronizedWakeLock mPreventScreenOnPartialLock; - private UnsynchronizedWakeLock mProximityPartialLock; - private HandlerThread mHandlerThread; - private Handler mScreenOffHandler; - private Handler mScreenBrightnessHandler; - private Handler mHandler; - private final TimeoutTask mTimeoutTask = new TimeoutTask(); - private ScreenBrightnessAnimator mScreenBrightnessAnimator; - private boolean mWaitingForFirstLightSensor = false; - private boolean mStillNeedSleepNotification; - private boolean mIsPowered = false; - private IActivityManager mActivityService; - private IBatteryStats mBatteryStats; - private BatteryService mBatteryService; - private SensorManager mSensorManager; - private Sensor mProximitySensor; - private Sensor mLightSensor; - private boolean mLightSensorEnabled; - private float mLightSensorValue = -1; - private boolean mProxIgnoredBecauseScreenTurnedOff = false; - private int mHighestLightSensorValue = -1; - private boolean mLightSensorPendingDecrease = false; - private boolean mLightSensorPendingIncrease = false; - private float mLightSensorPendingValue = -1; - private float mLightSensorAdjustSetting = 0; - private int mLightSensorScreenBrightness = -1; - private int mLightSensorButtonBrightness = -1; - private int mLightSensorKeyboardBrightness = -1; - private boolean mDimScreen = true; - private boolean mIsDocked = false; - private long mNextTimeout; - private volatile int mPokey = 0; - private volatile boolean mPokeAwakeOnSet = false; - private volatile boolean mInitComplete = false; - private final HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>(); - // mLastScreenOnTime is the time the screen was last turned on - private long mLastScreenOnTime; - private boolean mPreventScreenOn; - private int mScreenBrightnessSetting = DEFAULT_SCREEN_BRIGHTNESS; - private int mScreenBrightnessOverride = -1; - private int mButtonBrightnessOverride = -1; - private int mScreenBrightnessDim; - private boolean mUseSoftwareAutoBrightness; - private boolean mAutoBrightessEnabled; - private int[] mAutoBrightnessLevels; - private int[] mLcdBacklightValues; - private int[] mButtonBacklightValues; - private int[] mKeyboardBacklightValues; - private int mLightSensorWarmupTime; - boolean mUnplugTurnsOnScreen; - private int mWarningSpewThrottleCount; - private long mWarningSpewThrottleTime; - private int mAnimationSetting = ANIM_SETTING_OFF; - private float mWindowScaleAnimation; - - // Must match with the ISurfaceComposer constants in C++. - private static final int ANIM_SETTING_ON = 0x01; - private static final int ANIM_SETTING_OFF = 0x10; - - // Used when logging number and duration of touch-down cycles - private long mTotalTouchDownTime; - private long mLastTouchDown; - private int mTouchCycles; - - // could be either static or controllable at runtime - private static final boolean mSpew = false; - private static final boolean mDebugProximitySensor = (false || mSpew); - private static final boolean mDebugLightSensor = (false || mSpew); - private static final boolean mDebugLightAnimation = (false || mSpew); - - private native void nativeInit(); - private native void nativeSetPowerState(boolean screenOn, boolean screenBright); - private native void nativeStartSurfaceFlingerAnimation(int mode); - private static native void nativeAcquireWakeLock(int lock, String id); - private static native void nativeReleaseWakeLock(String id); - private static native int nativeSetScreenState(boolean on); - private static native void nativeShutdown(); - private static native void nativeReboot(String reason) throws IOException; - - /* - static PrintStream mLog; - static { - try { - mLog = new PrintStream("/data/power.log"); - } - catch (FileNotFoundException e) { - android.util.Slog.e(TAG, "Life is hard", e); - } - } - static class Log { - static void d(String tag, String s) { - mLog.println(s); - android.util.Slog.d(tag, s); - } - static void i(String tag, String s) { - mLog.println(s); - android.util.Slog.i(tag, s); - } - static void w(String tag, String s) { - mLog.println(s); - android.util.Slog.w(tag, s); - } - static void e(String tag, String s) { - mLog.println(s); - android.util.Slog.e(tag, s); - } - } - */ - - /** - * This class works around a deadlock between the lock in PowerManager.WakeLock - * and our synchronizing on mLocks. PowerManager.WakeLock synchronizes on its - * mToken object so it can be accessed from any thread, but it calls into here - * with its lock held. This class is essentially a reimplementation of - * PowerManager.WakeLock, but without that extra synchronized block, because we'll - * only call it with our own locks held. - */ - private class UnsynchronizedWakeLock { - int mFlags; - String mTag; - IBinder mToken; - int mCount = 0; - boolean mRefCounted; - boolean mHeld; - - UnsynchronizedWakeLock(int flags, String tag, boolean refCounted) { - mFlags = flags; - mTag = tag; - mToken = new Binder(); - mRefCounted = refCounted; - } - - public void acquire() { - if (!mRefCounted || mCount++ == 0) { - long ident = Binder.clearCallingIdentity(); - try { - PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, - MY_UID, MY_PID, mTag, null); - mHeld = true; - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - public void release() { - if (!mRefCounted || --mCount == 0) { - PowerManagerService.this.releaseWakeLockLocked(mToken, 0, false); - mHeld = false; - } - if (mCount < 0) { - throw new RuntimeException("WakeLock under-locked " + mTag); - } - } - - public boolean isHeld() - { - return mHeld; - } - - public String toString() { - return "UnsynchronizedWakeLock(mFlags=0x" + Integer.toHexString(mFlags) - + " mCount=" + mCount + " mHeld=" + mHeld + ")"; - } - } - - private final class BatteryReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - synchronized (mLocks) { - boolean wasPowered = mIsPowered; - mIsPowered = mBatteryService.isPowered(); - - if (mIsPowered != wasPowered) { - // update mStayOnWhilePluggedIn wake lock - updateWakeLockLocked(); - - // treat plugging and unplugging the devices as a user activity. - // users find it disconcerting when they unplug the device - // and it shuts off right away. - // to avoid turning on the screen when unplugging, we only trigger - // user activity when screen was already on. - // temporarily set mUserActivityAllowed to true so this will work - // even when the keyguard is on. - // However, you can also set config_unplugTurnsOnScreen to have it - // turn on. Some devices want this because they don't have a - // charging LED. - synchronized (mLocks) { - if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0 || - mUnplugTurnsOnScreen) { - forceUserActivityLocked(); - } - } - } - } - } - } - - private final class BootCompletedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - bootCompleted(); - } - } - - private final class DockReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, - Intent.EXTRA_DOCK_STATE_UNDOCKED); - dockStateChanged(state); - } - } - - /** - * Set the setting that determines whether the device stays on when plugged in. - * The argument is a bit string, with each bit specifying a power source that, - * when the device is connected to that source, causes the device to stay on. - * See {@link android.os.BatteryManager} for the list of power sources that - * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC} - * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB} - * @param val an {@code int} containing the bits that specify which power sources - * should cause the device to stay on. - */ - public void setStayOnSetting(int val) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null); - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.STAY_ON_WHILE_PLUGGED_IN, val); - } - - public void setMaximumScreenOffTimeount(int timeMs) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS, null); - synchronized (mLocks) { - mMaximumScreenOffTimeout = timeMs; - // recalculate everything - setScreenOffTimeoutsLocked(); - } - } - - int getStayOnConditionsLocked() { - return mMaximumScreenOffTimeout <= 0 || mMaximumScreenOffTimeout == Integer.MAX_VALUE - ? mStayOnConditions : 0; - } - - private class SettingsObserver implements Observer { - private int getInt(String name, int defValue) { - ContentValues values = mSettings.getValues(name); - Integer iVal = values != null ? values.getAsInteger(Settings.System.VALUE) : null; - return iVal != null ? iVal : defValue; - } - - private float getFloat(String name, float defValue) { - ContentValues values = mSettings.getValues(name); - Float fVal = values != null ? values.getAsFloat(Settings.System.VALUE) : null; - return fVal != null ? fVal : defValue; - } - - public void update(Observable o, Object arg) { - synchronized (mLocks) { - // STAY_ON_WHILE_PLUGGED_IN, default to when plugged into AC - mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN, - BatteryManager.BATTERY_PLUGGED_AC); - updateWakeLockLocked(); - - // SCREEN_OFF_TIMEOUT, default to 15 seconds - mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT); - - // DIM_SCREEN - //mDimScreen = getInt(DIM_SCREEN) != 0; - - mScreenBrightnessSetting = getInt(SCREEN_BRIGHTNESS, DEFAULT_SCREEN_BRIGHTNESS); - mLightSensorAdjustSetting = 0; //getFloat(SCREEN_AUTO_BRIGHTNESS_ADJ, 0); - - // SCREEN_BRIGHTNESS_MODE, default to manual - setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)); - - // recalculate everything - setScreenOffTimeoutsLocked(); - - mWindowScaleAnimation = getFloat(WINDOW_ANIMATION_SCALE, 1.0f); - final float transitionScale = getFloat(TRANSITION_ANIMATION_SCALE, 1.0f); - mAnimationSetting = 0; - if (mWindowScaleAnimation > 0.5f) { - mAnimationSetting |= ANIM_SETTING_OFF; - } - if (transitionScale > 0.5f) { - // Uncomment this if you want the screen-on animation. - // mAnimationSetting |= ANIM_SETTING_ON; - } - } - } - } - - PowerManagerService() { - // Hack to get our uid... should have a func for this. - long token = Binder.clearCallingIdentity(); - MY_UID = Process.myUid(); - MY_PID = Process.myPid(); - Binder.restoreCallingIdentity(token); - - // assume nothing is on yet - mUserState = mPowerState = 0; - - // Add ourself to the Watchdog monitors. - Watchdog.getInstance().addMonitor(this); - - nativeInit(); - } - - private ContentQueryMap mSettings; - - void init(Context context, LightsService lights, IActivityManager activity, - BatteryService battery) { - mLightsService = lights; - mContext = context; - mActivityService = activity; - mBatteryStats = BatteryStatsService.getService(); - mBatteryService = battery; - - mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT); - mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS); - mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD); - mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); - mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0")); - - mInitComplete = false; - mScreenBrightnessAnimator = new ScreenBrightnessAnimator("mScreenBrightnessUpdaterThread", - Process.THREAD_PRIORITY_DISPLAY); - mScreenBrightnessAnimator.start(); - - synchronized (mScreenBrightnessAnimator) { - while (!mInitComplete) { - try { - mScreenBrightnessAnimator.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - } - - mInitComplete = false; - mHandlerThread = new HandlerThread("PowerManagerService") { - @Override - protected void onLooperPrepared() { - super.onLooperPrepared(); - initInThread(); - } - }; - mHandlerThread.start(); - - synchronized (mHandlerThread) { - while (!mInitComplete) { - try { - mHandlerThread.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - } - - synchronized (mLocks) { - updateNativePowerStateLocked(); - // We make sure to start out with the screen on due to user activity. - // (They did just boot their device, after all.) - forceUserActivityLocked(); - mInitialized = true; - } - } - - void initInThread() { - mHandler = new Handler(); - - mBroadcastWakeLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "sleep_broadcast", true); - mStayOnWhilePluggedInScreenDimLock = new UnsynchronizedWakeLock( - PowerManager.SCREEN_DIM_WAKE_LOCK, "StayOnWhilePluggedIn Screen Dim", false); - mStayOnWhilePluggedInPartialLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false); - mPreventScreenOnPartialLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false); - mProximityPartialLock = new UnsynchronizedWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false); - - mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); - mScreenOnIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF); - mScreenOffIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - - Resources resources = mContext.getResources(); - - mAnimateScreenLights = resources.getBoolean( - com.android.internal.R.bool.config_animateScreenLights); - - mUnplugTurnsOnScreen = resources.getBoolean( - com.android.internal.R.bool.config_unplugTurnsOnScreen); - - mScreenBrightnessDim = resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessDim); - - // read settings for auto-brightness - mUseSoftwareAutoBrightness = resources.getBoolean( - com.android.internal.R.bool.config_automatic_brightness_available); - if (mUseSoftwareAutoBrightness) { - mAutoBrightnessLevels = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLevels); - mLcdBacklightValues = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); - mButtonBacklightValues = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessButtonBacklightValues); - mKeyboardBacklightValues = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessKeyboardBacklightValues); - mLightSensorWarmupTime = resources.getInteger( - com.android.internal.R.integer.config_lightSensorWarmupTime); - } - - ContentResolver resolver = mContext.getContentResolver(); - Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null, - "(" + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?) or (" - + Settings.System.NAME + "=?)", - new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN, SCREEN_BRIGHTNESS, - SCREEN_BRIGHTNESS_MODE, /*SCREEN_AUTO_BRIGHTNESS_ADJ,*/ - WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE}, - null); - mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler); - SettingsObserver settingsObserver = new SettingsObserver(); - mSettings.addObserver(settingsObserver); - - // pretend that the settings changed so we will get their initial state - settingsObserver.update(mSettings, null); - - // register for the battery changed notifications - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - mContext.registerReceiver(new BatteryReceiver(), filter); - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BOOT_COMPLETED); - mContext.registerReceiver(new BootCompletedReceiver(), filter); - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_DOCK_EVENT); - mContext.registerReceiver(new DockReceiver(), filter); - - // Listen for secure settings changes - mContext.getContentResolver().registerContentObserver( - Settings.Secure.CONTENT_URI, true, - new ContentObserver(new Handler()) { - public void onChange(boolean selfChange) { - updateSettingsValues(); - } - }); - updateSettingsValues(); - - synchronized (mHandlerThread) { - mInitComplete = true; - mHandlerThread.notifyAll(); - } - } - - /** - * Low-level function turn the device off immediately, without trying - * to be clean. Most people should use - * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown. - */ - public static void lowLevelShutdown() { - nativeShutdown(); - } - - /** - * Low-level function to reboot the device. - * - * @param reason code to pass to the kernel (e.g. "recovery"), or null. - * @throws IOException if reboot fails for some reason (eg, lack of - * permission) - */ - public static void lowLevelReboot(String reason) throws IOException { - nativeReboot(reason); - } - - private class WakeLock implements IBinder.DeathRecipient - { - WakeLock(int f, IBinder b, String t, int u, int p) { - super(); - flags = f; - binder = b; - tag = t; - uid = u == MY_UID ? Process.SYSTEM_UID : u; - pid = p; - if (u != MY_UID || ( - !"KEEP_SCREEN_ON_FLAG".equals(tag) - && !"KeyInputQueue".equals(tag))) { - monitorType = (f & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK - ? BatteryStats.WAKE_TYPE_PARTIAL - : BatteryStats.WAKE_TYPE_FULL; - } else { - monitorType = -1; - } - try { - b.linkToDeath(this, 0); - } catch (RemoteException e) { - binderDied(); - } - } - public void binderDied() { - synchronized (mLocks) { - releaseWakeLockLocked(this.binder, 0, true); - } - } - final int flags; - final IBinder binder; - final String tag; - final int uid; - final int pid; - final int monitorType; - WorkSource ws; - boolean activated = true; - int minState; - } - - private void updateWakeLockLocked() { - final int stayOnConditions = getStayOnConditionsLocked(); - if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) { - // keep the device on if we're plugged in and mStayOnWhilePluggedIn is set. - mStayOnWhilePluggedInScreenDimLock.acquire(); - mStayOnWhilePluggedInPartialLock.acquire(); - } else { - mStayOnWhilePluggedInScreenDimLock.release(); - mStayOnWhilePluggedInPartialLock.release(); - } - } - - private boolean isScreenLock(int flags) - { - int n = flags & LOCK_MASK; - return n == PowerManager.FULL_WAKE_LOCK - || n == PowerManager.SCREEN_BRIGHT_WAKE_LOCK - || n == PowerManager.SCREEN_DIM_WAKE_LOCK - || n == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK; - } - - void enforceWakeSourcePermission(int uid, int pid) { - if (uid == Process.myUid()) { - return; - } - mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, - pid, uid, null); - } - - public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) { - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); - if (uid != Process.myUid()) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - } - if (ws != null) { - enforceWakeSourcePermission(uid, pid); - } - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mLocks) { - acquireWakeLockLocked(flags, lock, uid, pid, tag, ws); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - void noteStartWakeLocked(WakeLock wl, WorkSource ws) { - if (wl.monitorType >= 0) { - long origId = Binder.clearCallingIdentity(); - try { - if (ws != null) { - mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag, - wl.monitorType); - } else { - mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType); - } - } catch (RemoteException e) { - // Ignore - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - void noteStopWakeLocked(WakeLock wl, WorkSource ws) { - if (wl.monitorType >= 0) { - long origId = Binder.clearCallingIdentity(); - try { - if (ws != null) { - mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag, - wl.monitorType); - } else { - mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType); - } - } catch (RemoteException e) { - // Ignore - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - - public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag, - WorkSource ws) { - if (mSpew) { - Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag); - } - - if (ws != null && ws.size() == 0) { - ws = null; - } - - int index = mLocks.getIndex(lock); - WakeLock wl; - boolean newlock; - boolean diffsource; - WorkSource oldsource; - if (index < 0) { - wl = new WakeLock(flags, lock, tag, uid, pid); - switch (wl.flags & LOCK_MASK) - { - case PowerManager.FULL_WAKE_LOCK: - if (mUseSoftwareAutoBrightness) { - wl.minState = SCREEN_BRIGHT; - } else { - wl.minState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT); - } - break; - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - wl.minState = SCREEN_BRIGHT; - break; - case PowerManager.SCREEN_DIM_WAKE_LOCK: - wl.minState = SCREEN_DIM; - break; - case PowerManager.PARTIAL_WAKE_LOCK: - case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: - break; - default: - // just log and bail. we're in the server, so don't - // throw an exception. - Slog.e(TAG, "bad wakelock type for lock '" + tag + "' " - + " flags=" + flags); - return; - } - mLocks.addLock(wl); - if (ws != null) { - wl.ws = new WorkSource(ws); - } - newlock = true; - diffsource = false; - oldsource = null; - } else { - wl = mLocks.get(index); - newlock = false; - oldsource = wl.ws; - if (oldsource != null) { - if (ws == null) { - wl.ws = null; - diffsource = true; - } else { - diffsource = oldsource.diff(ws); - } - } else if (ws != null) { - diffsource = true; - } else { - diffsource = false; - } - if (diffsource) { - wl.ws = new WorkSource(ws); - } - } - if (isScreenLock(flags)) { - // if this causes a wakeup, we reactivate all of the locks and - // set it to whatever they want. otherwise, we modulate that - // by the current state so we never turn it more on than - // it already is. - if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { - mProximityWakeLockCount++; - if (mProximityWakeLockCount == 1) { - enableProximityLockLocked(); - } - } else { - if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { - int oldWakeLockState = mWakeLockState; - mWakeLockState = mLocks.reactivateScreenLocksLocked(); - - // Disable proximity sensor if if user presses power key while we are in the - // "waiting for proximity sensor to go negative" state. - if ((mWakeLockState & SCREEN_ON_BIT) != 0 - && mProximitySensorActive && mProximityWakeLockCount == 0) { - mProximitySensorActive = false; - } - - if (mSpew) { - Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState) - + " mWakeLockState=0x" - + Integer.toHexString(mWakeLockState) - + " previous wakeLockState=0x" - + Integer.toHexString(oldWakeLockState)); - } - } else { - if (mSpew) { - Slog.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState) - + " mLocks.gatherState()=0x" - + Integer.toHexString(mLocks.gatherState()) - + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)); - } - mWakeLockState = (mUserState | mWakeLockState) & mLocks.gatherState(); - } - setPowerState(mWakeLockState | mUserState); - } - } - else if ((flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) { - if (newlock) { - mPartialCount++; - if (mPartialCount == 1) { - if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 1, tag); - } - } - nativeAcquireWakeLock(PARTIAL_WAKE_LOCK_ID, PARTIAL_NAME); - } - - if (diffsource) { - // If the lock sources have changed, need to first release the - // old ones. - noteStopWakeLocked(wl, oldsource); - } - if (newlock || diffsource) { - noteStartWakeLocked(wl, ws); - } - } - - public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) { - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); - if (ws != null && ws.size() == 0) { - ws = null; - } - if (ws != null) { - enforceWakeSourcePermission(uid, pid); - } - synchronized (mLocks) { - int index = mLocks.getIndex(lock); - if (index < 0) { - throw new IllegalArgumentException("Wake lock not active"); - } - WakeLock wl = mLocks.get(index); - WorkSource oldsource = wl.ws; - wl.ws = ws != null ? new WorkSource(ws) : null; - noteStopWakeLocked(wl, oldsource); - noteStartWakeLocked(wl, ws); - } - } - - public void releaseWakeLock(IBinder lock, int flags) { - int uid = Binder.getCallingUid(); - if (uid != Process.myUid()) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - } - - synchronized (mLocks) { - releaseWakeLockLocked(lock, flags, false); - } - } - - private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) { - WakeLock wl = mLocks.removeLock(lock); - if (wl == null) { - return; - } - - if (mSpew) { - Slog.d(TAG, "releaseWakeLock flags=0x" - + Integer.toHexString(wl.flags) + " tag=" + wl.tag); - } - - if (isScreenLock(wl.flags)) { - if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { - mProximityWakeLockCount--; - if (mProximityWakeLockCount == 0) { - if (mProximitySensorActive && - ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0)) { - // wait for proximity sensor to go negative before disabling sensor - if (mDebugProximitySensor) { - Slog.d(TAG, "waiting for proximity sensor to go negative"); - } - } else { - disableProximityLockLocked(); - } - } - } else { - mWakeLockState = mLocks.gatherState(); - // goes in the middle to reduce flicker - if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) { - userActivity(SystemClock.uptimeMillis(), -1, false, OTHER_EVENT, false, true); - } - setPowerState(mWakeLockState | mUserState); - } - } - else if ((wl.flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) { - mPartialCount--; - if (mPartialCount == 0) { - if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 0, wl.tag); - nativeReleaseWakeLock(PARTIAL_NAME); - } - } - // Unlink the lock from the binder. - wl.binder.unlinkToDeath(wl, 0); - - noteStopWakeLocked(wl, wl.ws); - } - - private class PokeLock implements IBinder.DeathRecipient - { - PokeLock(int p, IBinder b, String t) { - super(); - this.pokey = p; - this.binder = b; - this.tag = t; - try { - b.linkToDeath(this, 0); - } catch (RemoteException e) { - binderDied(); - } - } - public void binderDied() { - setPokeLock(0, this.binder, this.tag); - } - int pokey; - IBinder binder; - String tag; - boolean awakeOnSet; - } - - public void setPokeLock(int pokey, IBinder token, String tag) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - if (token == null) { - Slog.e(TAG, "setPokeLock got null token for tag='" + tag + "'"); - return; - } - - if ((pokey & POKE_LOCK_TIMEOUT_MASK) == POKE_LOCK_TIMEOUT_MASK) { - throw new IllegalArgumentException("setPokeLock can't have both POKE_LOCK_SHORT_TIMEOUT" - + " and POKE_LOCK_MEDIUM_TIMEOUT"); - } - - synchronized (mLocks) { - if (pokey != 0) { - PokeLock p = mPokeLocks.get(token); - int oldPokey = 0; - if (p != null) { - oldPokey = p.pokey; - p.pokey = pokey; - } else { - p = new PokeLock(pokey, token, tag); - mPokeLocks.put(token, p); - } - int oldTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK; - int newTimeout = pokey & POKE_LOCK_TIMEOUT_MASK; - if (((mPowerState & SCREEN_ON_BIT) == 0) && (oldTimeout != newTimeout)) { - p.awakeOnSet = true; - } - } else { - PokeLock rLock = mPokeLocks.remove(token); - if (rLock != null) { - token.unlinkToDeath(rLock, 0); - } - } - - int oldPokey = mPokey; - int cumulative = 0; - boolean awakeOnSet = false; - for (PokeLock p: mPokeLocks.values()) { - cumulative |= p.pokey; - if (p.awakeOnSet) { - awakeOnSet = true; - } - } - mPokey = cumulative; - mPokeAwakeOnSet = awakeOnSet; - - int oldCumulativeTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK; - int newCumulativeTimeout = pokey & POKE_LOCK_TIMEOUT_MASK; - - if (oldCumulativeTimeout != newCumulativeTimeout) { - setScreenOffTimeoutsLocked(); - // reset the countdown timer, but use the existing nextState so it doesn't - // change anything - setTimeoutLocked(SystemClock.uptimeMillis(), mTimeoutTask.nextState); - } - } - } - - private static String lockType(int type) - { - switch (type) - { - case PowerManager.FULL_WAKE_LOCK: - return "FULL_WAKE_LOCK "; - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - return "SCREEN_BRIGHT_WAKE_LOCK "; - case PowerManager.SCREEN_DIM_WAKE_LOCK: - return "SCREEN_DIM_WAKE_LOCK "; - case PowerManager.PARTIAL_WAKE_LOCK: - return "PARTIAL_WAKE_LOCK "; - case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: - return "PROXIMITY_SCREEN_OFF_WAKE_LOCK"; - default: - return "??? "; - } - } - - private static String dumpPowerState(int state) { - return (((state & KEYBOARD_BRIGHT_BIT) != 0) - ? "KEYBOARD_BRIGHT_BIT " : "") - + (((state & SCREEN_BRIGHT_BIT) != 0) - ? "SCREEN_BRIGHT_BIT " : "") - + (((state & SCREEN_ON_BIT) != 0) - ? "SCREEN_ON_BIT " : "") - + (((state & BUTTON_BRIGHT_BIT) != 0) - ? "BUTTON_BRIGHT_BIT " : "") - + (((state & BATTERY_LOW_BIT) != 0) - ? "BATTERY_LOW_BIT " : ""); - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump PowerManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - long now = SystemClock.uptimeMillis(); - - synchronized (mLocks) { - pw.println("Power Manager State:"); - pw.println(" mIsPowered=" + mIsPowered - + " mPowerState=" + mPowerState - + " mScreenOffTime=" + (SystemClock.elapsedRealtime()-mScreenOffTime) - + " ms"); - pw.println(" mPartialCount=" + mPartialCount); - pw.println(" mWakeLockState=" + dumpPowerState(mWakeLockState)); - pw.println(" mUserState=" + dumpPowerState(mUserState)); - pw.println(" mPowerState=" + dumpPowerState(mPowerState)); - pw.println(" mLocks.gather=" + dumpPowerState(mLocks.gatherState())); - pw.println(" mNextTimeout=" + mNextTimeout + " now=" + now - + " " + ((mNextTimeout-now)/1000) + "s from now"); - pw.println(" mDimScreen=" + mDimScreen - + " mStayOnConditions=" + mStayOnConditions - + " mPreparingForScreenOn=" + mPreparingForScreenOn - + " mSkippedScreenOn=" + mSkippedScreenOn); - pw.println(" mScreenOffReason=" + mScreenOffReason - + " mUserState=" + mUserState); - pw.println(" mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1] - + ',' + mBroadcastQueue[2] + "}"); - pw.println(" mBroadcastWhy={" + mBroadcastWhy[0] + ',' + mBroadcastWhy[1] - + ',' + mBroadcastWhy[2] + "}"); - pw.println(" mPokey=" + mPokey + " mPokeAwakeonSet=" + mPokeAwakeOnSet); - pw.println(" mKeyboardVisible=" + mKeyboardVisible - + " mUserActivityAllowed=" + mUserActivityAllowed); - pw.println(" mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay - + " mScreenOffDelay=" + mScreenOffDelay); - pw.println(" mPreventScreenOn=" + mPreventScreenOn - + " mScreenBrightnessOverride=" + mScreenBrightnessOverride - + " mButtonBrightnessOverride=" + mButtonBrightnessOverride); - pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting - + " mMaximumScreenOffTimeout=" + mMaximumScreenOffTimeout); - pw.println(" mLastScreenOnTime=" + mLastScreenOnTime); - pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock); - pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock); - pw.println(" mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock); - pw.println(" mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock); - pw.println(" mProximityPartialLock=" + mProximityPartialLock); - pw.println(" mProximityWakeLockCount=" + mProximityWakeLockCount); - pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); - pw.println(" mProximitySensorActive=" + mProximitySensorActive); - pw.println(" mProximityPendingValue=" + mProximityPendingValue); - pw.println(" mLastProximityEventTime=" + mLastProximityEventTime); - pw.println(" mLightSensorEnabled=" + mLightSensorEnabled - + " mLightSensorAdjustSetting=" + mLightSensorAdjustSetting); - pw.println(" mLightSensorValue=" + mLightSensorValue - + " mLightSensorPendingValue=" + mLightSensorPendingValue); - pw.println(" mHighestLightSensorValue=" + mHighestLightSensorValue - + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor); - pw.println(" mLightSensorPendingDecrease=" + mLightSensorPendingDecrease - + " mLightSensorPendingIncrease=" + mLightSensorPendingIncrease); - pw.println(" mLightSensorScreenBrightness=" + mLightSensorScreenBrightness - + " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness - + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness); - pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness); - pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled); - mScreenBrightnessAnimator.dump(pw, "mScreenBrightnessAnimator: "); - - int N = mLocks.size(); - pw.println(); - pw.println("mLocks.size=" + N + ":"); - for (int i=0; i<N; i++) { - WakeLock wl = mLocks.get(i); - String type = lockType(wl.flags & LOCK_MASK); - String acquireCausesWakeup = ""; - if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { - acquireCausesWakeup = "ACQUIRE_CAUSES_WAKEUP "; - } - String activated = ""; - if (wl.activated) { - activated = " activated"; - } - pw.println(" " + type + " '" + wl.tag + "'" + acquireCausesWakeup - + activated + " (minState=" + wl.minState + ", uid=" + wl.uid - + ", pid=" + wl.pid + ")"); - } - - pw.println(); - pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":"); - for (PokeLock p: mPokeLocks.values()) { - pw.println(" poke lock '" + p.tag + "':" - + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0 - ? " POKE_LOCK_IGNORE_TOUCH_EVENTS" : "") - + ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0 - ? " POKE_LOCK_SHORT_TIMEOUT" : "") - + ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0 - ? " POKE_LOCK_MEDIUM_TIMEOUT" : "")); - } - - pw.println(); - } - } - - private void setTimeoutLocked(long now, int nextState) { - setTimeoutLocked(now, -1, nextState); - } - - // If they gave a timeoutOverride it is the number of seconds - // to screen-off. Figure out where in the countdown cycle we - // should jump to. - private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) { - long timeoutOverride = originalTimeoutOverride; - if (mBootCompleted) { - synchronized (mLocks) { - long when = 0; - if (timeoutOverride <= 0) { - switch (nextState) - { - case SCREEN_BRIGHT: - when = now + mKeylightDelay; - break; - case SCREEN_DIM: - if (mDimDelay >= 0) { - when = now + mDimDelay; - break; - } else { - Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim"); - } - case SCREEN_OFF: - synchronized (mLocks) { - when = now + mScreenOffDelay; - } - break; - default: - when = now; - break; - } - } else { - override: { - if (timeoutOverride <= mScreenOffDelay) { - when = now + timeoutOverride; - nextState = SCREEN_OFF; - break override; - } - timeoutOverride -= mScreenOffDelay; - - if (mDimDelay >= 0) { - if (timeoutOverride <= mDimDelay) { - when = now + timeoutOverride; - nextState = SCREEN_DIM; - break override; - } - timeoutOverride -= mDimDelay; - } - - when = now + timeoutOverride; - nextState = SCREEN_BRIGHT; - } - } - if (mSpew) { - Slog.d(TAG, "setTimeoutLocked now=" + now - + " timeoutOverride=" + timeoutOverride - + " nextState=" + nextState + " when=" + when); - } - - mHandler.removeCallbacks(mTimeoutTask); - mTimeoutTask.nextState = nextState; - mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0 - ? (originalTimeoutOverride - timeoutOverride) - : -1; - mHandler.postAtTime(mTimeoutTask, when); - mNextTimeout = when; // for debugging - } - } - } - - private void cancelTimerLocked() - { - mHandler.removeCallbacks(mTimeoutTask); - mTimeoutTask.nextState = -1; - } - - private class TimeoutTask implements Runnable - { - int nextState; // access should be synchronized on mLocks - long remainingTimeoutOverride; - public void run() - { - synchronized (mLocks) { - if (mSpew) { - Slog.d(TAG, "user activity timeout timed out nextState=" + this.nextState); - } - - if (nextState == -1) { - return; - } - - mUserState = this.nextState; - setPowerState(this.nextState | mWakeLockState); - - long now = SystemClock.uptimeMillis(); - - switch (this.nextState) - { - case SCREEN_BRIGHT: - if (mDimDelay >= 0) { - setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM); - break; - } - case SCREEN_DIM: - setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF); - break; - } - } - } - } - - private void sendNotificationLocked(boolean on, int why) { - if (!mInitialized) { - // No notifications sent until first initialization is done. - // This is so that when we are moving from our initial state - // which looks like the screen was off to it being on, we do not - // go through the process of waiting for the higher-level user - // space to be ready before turning up the display brightness. - // (And also do not send needless broadcasts about the screen.) - return; - } - - if (DEBUG_SCREEN_ON) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "sendNotificationLocked: " + on, here); - } - - if (!on) { - mStillNeedSleepNotification = false; - } - - // Add to the queue. - int index = 0; - while (mBroadcastQueue[index] != -1) { - index++; - } - mBroadcastQueue[index] = on ? 1 : 0; - mBroadcastWhy[index] = why; - - // If we added it position 2, then there is a pair that can be stripped. - // If we added it position 1 and we're turning the screen off, we can strip - // the pair and do nothing, because the screen is already off, and therefore - // keyguard has already been enabled. - // However, if we added it at position 1 and we're turning it on, then position - // 0 was to turn it off, and we can't strip that, because keyguard needs to come - // on, so have to run the queue then. - if (index == 2) { - // While we're collapsing them, if it's going off, and the new reason - // is more significant than the first, then use the new one. - if (!on && mBroadcastWhy[0] > why) { - mBroadcastWhy[0] = why; - } - mBroadcastQueue[0] = on ? 1 : 0; - mBroadcastQueue[1] = -1; - mBroadcastQueue[2] = -1; - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - index = 0; - } - if (index == 1 && !on) { - mBroadcastQueue[0] = -1; - mBroadcastQueue[1] = -1; - index = -1; - // The wake lock was being held, but we're not actually going to do any - // broadcasts, so release the wake lock. - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - - // The broadcast queue has changed; make sure the screen is on if it - // is now possible for it to be. - if (mSkippedScreenOn) { - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - } - - // Now send the message. - if (index >= 0) { - // Acquire the broadcast wake lock before changing the power - // state. It will be release after the broadcast is sent. - // We always increment the ref count for each notification in the queue - // and always decrement when that notification is handled. - mBroadcastWakeLock.acquire(); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount); - mHandler.post(mNotificationTask); - } - } - - private WindowManagerPolicy.ScreenOnListener mScreenOnListener = - new WindowManagerPolicy.ScreenOnListener() { - public void onScreenOn() { - synchronized (mLocks) { - if (mPreparingForScreenOn) { - mPreparingForScreenOn = false; - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, - 4, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - } - } - }; - - private Runnable mNotificationTask = new Runnable() - { - public void run() - { - while (true) { - int value; - int why; - WindowManagerPolicy policy; - synchronized (mLocks) { - value = mBroadcastQueue[0]; - why = mBroadcastWhy[0]; - for (int i=0; i<2; i++) { - mBroadcastQueue[i] = mBroadcastQueue[i+1]; - mBroadcastWhy[i] = mBroadcastWhy[i+1]; - } - policy = getPolicyLocked(); - if (value == 1 && !mPreparingForScreenOn) { - mPreparingForScreenOn = true; - mBroadcastWakeLock.acquire(); - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, - mBroadcastWakeLock.mCount); - } - } - if (value == 1) { - mScreenOnStart = SystemClock.uptimeMillis(); - - policy.screenTurningOn(mScreenOnListener); - try { - ActivityManagerNative.getDefault().wakingUp(); - } catch (RemoteException e) { - // ignore it - } - - if (mSpew) { - Slog.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock); - } - if (mContext != null && ActivityManagerNative.isSystemReady()) { - mContext.sendOrderedBroadcast(mScreenOnIntent, null, - mScreenOnBroadcastDone, mHandler, 0, null, null); - } else { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, - mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - } - } - else if (value == 0) { - mScreenOffStart = SystemClock.uptimeMillis(); - - policy.screenTurnedOff(why); - try { - ActivityManagerNative.getDefault().goingToSleep(); - } catch (RemoteException e) { - // ignore it. - } - - if (mContext != null && ActivityManagerNative.isSystemReady()) { - mContext.sendOrderedBroadcast(mScreenOffIntent, null, - mScreenOffBroadcastDone, mHandler, 0, null, null); - } else { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, - mBroadcastWakeLock.mCount); - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - mBroadcastWakeLock.release(); - } - } - } - else { - // If we're in this case, then this handler is running for a previous - // paired transaction. mBroadcastWakeLock will already have been released. - break; - } - } - } - }; - - long mScreenOnStart; - private BroadcastReceiver mScreenOnBroadcastDone = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1, - SystemClock.uptimeMillis() - mScreenOnStart, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - } - }; - - long mScreenOffStart; - private BroadcastReceiver mScreenOffBroadcastDone = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - synchronized (mLocks) { - EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0, - SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } - } - }; - - void logPointerUpEvent() { - if (LOG_TOUCH_DOWNS) { - mTotalTouchDownTime += SystemClock.elapsedRealtime() - mLastTouchDown; - mLastTouchDown = 0; - } - } - - void logPointerDownEvent() { - if (LOG_TOUCH_DOWNS) { - // If we are not already timing a down/up sequence - if (mLastTouchDown == 0) { - mLastTouchDown = SystemClock.elapsedRealtime(); - mTouchCycles++; - } - } - } - - /** - * Prevents the screen from turning on even if it *should* turn on due - * to a subsequent full wake lock being acquired. - * <p> - * This is a temporary hack that allows an activity to "cover up" any - * display glitches that happen during the activity's startup - * sequence. (Specifically, this API was added to work around a - * cosmetic bug in the "incoming call" sequence, where the lock screen - * would flicker briefly before the incoming call UI became visible.) - * TODO: There ought to be a more elegant way of doing this, - * probably by having the PowerManager and ActivityManager - * work together to let apps specify that the screen on/off - * state should be synchronized with the Activity lifecycle. - * <p> - * Note that calling preventScreenOn(true) will NOT turn the screen - * off if it's currently on. (This API only affects *future* - * acquisitions of full wake locks.) - * But calling preventScreenOn(false) WILL turn the screen on if - * it's currently off because of a prior preventScreenOn(true) call. - * <p> - * Any call to preventScreenOn(true) MUST be followed promptly by a call - * to preventScreenOn(false). In fact, if the preventScreenOn(false) - * call doesn't occur within 5 seconds, we'll turn the screen back on - * ourselves (and log a warning about it); this prevents a buggy app - * from disabling the screen forever.) - * <p> - * TODO: this feature should really be controlled by a new type of poke - * lock (rather than an IPowerManager call). - */ - public void preventScreenOn(boolean prevent) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - - synchronized (mLocks) { - if (prevent) { - // First of all, grab a partial wake lock to - // make sure the CPU stays on during the entire - // preventScreenOn(true) -> preventScreenOn(false) sequence. - mPreventScreenOnPartialLock.acquire(); - - // Post a forceReenableScreen() call (for 5 seconds in the - // future) to make sure the matching preventScreenOn(false) call - // has happened by then. - mHandler.removeCallbacks(mForceReenableScreenTask); - mHandler.postDelayed(mForceReenableScreenTask, 5000); - - // Finally, set the flag that prevents the screen from turning on. - // (Below, in setPowerState(), we'll check mPreventScreenOn and - // we *won't* call setScreenStateLocked(true) if it's set.) - mPreventScreenOn = true; - } else { - // (Re)enable the screen. - mPreventScreenOn = false; - - // We're "undoing" a the prior preventScreenOn(true) call, so we - // no longer need the 5-second safeguard. - mHandler.removeCallbacks(mForceReenableScreenTask); - - // Forcibly turn on the screen if it's supposed to be on. (This - // handles the case where the screen is currently off because of - // a prior preventScreenOn(true) call.) - if (!mProximitySensorActive && (mPowerState & SCREEN_ON_BIT) != 0) { - if (mSpew) { - Slog.d(TAG, - "preventScreenOn: turning on after a prior preventScreenOn(true)!"); - } - int err = setScreenStateLocked(true); - if (err != 0) { - Slog.w(TAG, "preventScreenOn: error from setScreenStateLocked(): " + err); - } - } - - // Release the partial wake lock that we held during the - // preventScreenOn(true) -> preventScreenOn(false) sequence. - mPreventScreenOnPartialLock.release(); - } - } - } - - public void setScreenBrightnessOverride(int brightness) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - - if (mSpew) Slog.d(TAG, "setScreenBrightnessOverride " + brightness); - synchronized (mLocks) { - if (mScreenBrightnessOverride != brightness) { - mScreenBrightnessOverride = brightness; - if (isScreenOn()) { - updateLightsLocked(mPowerState, SCREEN_ON_BIT); - } - } - } - } - - public void setButtonBrightnessOverride(int brightness) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - - if (mSpew) Slog.d(TAG, "setButtonBrightnessOverride " + brightness); - synchronized (mLocks) { - if (mButtonBrightnessOverride != brightness) { - mButtonBrightnessOverride = brightness; - if (isScreenOn()) { - updateLightsLocked(mPowerState, BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT); - } - } - } - } - - /** - * Sanity-check that gets called 5 seconds after any call to - * preventScreenOn(true). This ensures that the original call - * is followed promptly by a call to preventScreenOn(false). - */ - private void forceReenableScreen() { - // We shouldn't get here at all if mPreventScreenOn is false, since - // we should have already removed any existing - // mForceReenableScreenTask messages... - if (!mPreventScreenOn) { - Slog.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do"); - return; - } - - // Uh oh. It's been 5 seconds since a call to - // preventScreenOn(true) and we haven't re-enabled the screen yet. - // This means the app that called preventScreenOn(true) is either - // slow (i.e. it took more than 5 seconds to call preventScreenOn(false)), - // or buggy (i.e. it forgot to call preventScreenOn(false), or - // crashed before doing so.) - - // Log a warning, and forcibly turn the screen back on. - Slog.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! " - + "Forcing the screen back on..."); - preventScreenOn(false); - } - - private Runnable mForceReenableScreenTask = new Runnable() { - public void run() { - forceReenableScreen(); - } - }; - - private int setScreenStateLocked(boolean on) { - if (DEBUG_SCREEN_ON) { - RuntimeException e = new RuntimeException("here"); - e.fillInStackTrace(); - Slog.i(TAG, "Set screen state: " + on, e); - } - if (on) { - if (mInitialized && ((mPowerState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn)) { - // If we are turning the screen state on, but the screen - // light is currently off, then make sure that we set the - // light at this point to 0. This is the case where we are - // turning on the screen and waiting for the UI to be drawn - // before showing it to the user. We want the light off - // until it is ready to be shown to the user, not it using - // whatever the last value it had. - // Skip this if the screen is being turned on for the first time - // after boot (mInitialized is false). - if (DEBUG_SCREEN_ON) { - Slog.i(TAG, "Forcing brightness 0: mPowerState=0x" - + Integer.toHexString(mPowerState) - + " mSkippedScreenOn=" + mSkippedScreenOn); - } - mScreenBrightnessAnimator.animateTo(PowerManager.BRIGHTNESS_OFF, SCREEN_BRIGHT_BIT, 0); - } - } - int err = nativeSetScreenState(on); - if (err == 0) { - mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0); - if (mUseSoftwareAutoBrightness) { - enableLightSensorLocked(on); - if (on) { - // If AutoBrightness is enabled, set the brightness immediately after the - // next sensor value is received. - mWaitingForFirstLightSensor = mAutoBrightessEnabled; - } else { - // make sure button and key backlights are off too - mButtonLight.turnOff(); - mKeyboardLight.turnOff(); - } - } - } - return err; - } - - private void setPowerState(int state) - { - setPowerState(state, false, WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT); - } - - private void setPowerState(int newState, boolean noChangeLights, int reason) - { - synchronized (mLocks) { - int err; - - if (mSpew) { - Slog.d(TAG, "setPowerState: mPowerState=0x" + Integer.toHexString(mPowerState) - + " newState=0x" + Integer.toHexString(newState) - + " noChangeLights=" + noChangeLights - + " reason=" + reason); - } - - if (noChangeLights) { - newState = (newState & ~LIGHTS_MASK) | (mPowerState & LIGHTS_MASK); - } - if (mProximitySensorActive) { - // don't turn on the screen when the proximity sensor lock is held - newState = (newState & ~SCREEN_BRIGHT); - } - - if (batteryIsLow()) { - newState |= BATTERY_LOW_BIT; - } else { - newState &= ~BATTERY_LOW_BIT; - } - if (newState == mPowerState && mInitialized) { - return; - } - - if (!mBootCompleted && !mUseSoftwareAutoBrightness) { - newState |= ALL_BRIGHT; - } - - boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0; - boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0; - - if (mSpew) { - Slog.d(TAG, "setPowerState: mPowerState=" + mPowerState - + " newState=" + newState + " noChangeLights=" + noChangeLights); - Slog.d(TAG, " oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0) - + " newKeyboardBright=" + ((newState & KEYBOARD_BRIGHT_BIT) != 0)); - Slog.d(TAG, " oldScreenBright=" + ((mPowerState & SCREEN_BRIGHT_BIT) != 0) - + " newScreenBright=" + ((newState & SCREEN_BRIGHT_BIT) != 0)); - Slog.d(TAG, " oldButtonBright=" + ((mPowerState & BUTTON_BRIGHT_BIT) != 0) - + " newButtonBright=" + ((newState & BUTTON_BRIGHT_BIT) != 0)); - Slog.d(TAG, " oldScreenOn=" + oldScreenOn - + " newScreenOn=" + newScreenOn); - Slog.d(TAG, " oldBatteryLow=" + ((mPowerState & BATTERY_LOW_BIT) != 0) - + " newBatteryLow=" + ((newState & BATTERY_LOW_BIT) != 0)); - } - - final boolean stateChanged = mPowerState != newState; - - if (stateChanged && reason == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT) { - if (mPolicy != null && mPolicy.isScreenSaverEnabled()) { - if (mSpew) { - Slog.d(TAG, "setPowerState: running screen saver instead of turning off screen"); - } - if (mPolicy.startScreenSaver()) { - // was successful - return; - } - } - } - - - if (oldScreenOn != newScreenOn) { - if (newScreenOn) { - // When the user presses the power button, we need to always send out the - // notification that it's going to sleep so the keyguard goes on. But - // we can't do that until the screen fades out, so we don't show the keyguard - // too early. - if (mStillNeedSleepNotification) { - sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER); - } - - // Turn on the screen UNLESS there was a prior - // preventScreenOn(true) request. (Note that the lifetime - // of a single preventScreenOn() request is limited to 5 - // seconds to prevent a buggy app from disabling the - // screen forever; see forceReenableScreen().) - boolean reallyTurnScreenOn = true; - if (mSpew) { - Slog.d(TAG, "- turning screen on... mPreventScreenOn = " - + mPreventScreenOn); - } - - if (mPreventScreenOn) { - if (mSpew) { - Slog.d(TAG, "- PREVENTING screen from really turning on!"); - } - reallyTurnScreenOn = false; - } - if (reallyTurnScreenOn) { - err = setScreenStateLocked(true); - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(getPreferredBrightness()); - mBatteryStats.noteScreenOn(); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - } else { - setScreenStateLocked(false); - // But continue as if we really did turn the screen on... - err = 0; - } - - mLastTouchDown = 0; - mTotalTouchDownTime = 0; - mTouchCycles = 0; - EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, reason, - mTotalTouchDownTime, mTouchCycles); - if (err == 0) { - sendNotificationLocked(true, -1); - // Update the lights *after* taking care of turning the - // screen on, so we do this after our notifications are - // enqueued and thus will delay turning on the screen light - // until the windows are correctly displayed. - if (stateChanged) { - updateLightsLocked(newState, 0); - } - mPowerState |= SCREEN_ON_BIT; - } - - } else { - // Update the lights *before* taking care of turning the - // screen off, so we can initiate any animations that are desired. - mScreenOffReason = reason; - if (stateChanged) { - updateLightsLocked(newState, 0); - } - - // cancel light sensor task - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - mScreenOffTime = SystemClock.elapsedRealtime(); - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenOff(); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - mPowerState &= ~SCREEN_ON_BIT; - if (!mScreenBrightnessAnimator.isAnimating()) { - err = screenOffFinishedAnimatingLocked(reason); - } else { - err = 0; - mLastTouchDown = 0; - } - } - } else if (stateChanged) { - // Screen on/off didn't change, but lights may have. - updateLightsLocked(newState, 0); - } - - mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK); - - updateNativePowerStateLocked(); - } - } - - private void updateNativePowerStateLocked() { - if (!mHeadless) { - nativeSetPowerState( - (mPowerState & SCREEN_ON_BIT) != 0, - (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT); - } - } - - private int screenOffFinishedAnimatingLocked(int reason) { - // I don't think we need to check the current state here because all of these - // Power.setScreenState and sendNotificationLocked can both handle being - // called multiple times in the same state. -joeo - EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime, - mTouchCycles); - mLastTouchDown = 0; - int err = setScreenStateLocked(false); - if (err == 0) { - mScreenOffReason = reason; - sendNotificationLocked(false, reason); - } - return err; - } - - private boolean batteryIsLow() { - return (!mIsPowered && - mBatteryService.getBatteryLevel() <= LOW_BATTERY_THRESHOLD); - } - - private boolean shouldDeferScreenOnLocked() { - if (mPreparingForScreenOn) { - // Currently waiting for confirmation from the policy that it - // is okay to turn on the screen. Don't allow the screen to go - // on until that is done. - if (DEBUG_SCREEN_ON) Slog.i(TAG, - "updateLights: delaying screen on due to mPreparingForScreenOn"); - return true; - } else { - // If there is a screen-on command in the notification queue, we - // can't turn the screen on until it has been processed (and we - // have set mPreparingForScreenOn) or it has been dropped. - for (int i=0; i<mBroadcastQueue.length; i++) { - if (mBroadcastQueue[i] == 1) { - if (DEBUG_SCREEN_ON) Slog.i(TAG, - "updateLights: delaying screen on due to notification queue"); - return true; - } - } - } - return false; - } - - private void updateLightsLocked(int newState, int forceState) { - final int oldState = mPowerState; - - // If the screen is not currently on, we will want to delay actually - // turning the lights on if we are still getting the UI put up. - if ((oldState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn) { - // Don't turn screen on until we know we are really ready to. - // This is to avoid letting the screen go on before things like the - // lock screen have been displayed. - if ((mSkippedScreenOn = shouldDeferScreenOnLocked())) { - newState &= ~(SCREEN_ON_BIT|SCREEN_BRIGHT_BIT); - } - } - - if ((newState & SCREEN_ON_BIT) != 0) { - // Only turn on the buttons or keyboard if the screen is also on. - // We should never see the buttons on but not the screen. - newState = applyButtonState(newState); - newState = applyKeyboardState(newState); - } - final int realDifference = (newState ^ oldState); - final int difference = realDifference | forceState; - if (difference == 0) { - return; - } - - int offMask = 0; - int dimMask = 0; - int onMask = 0; - - int preferredBrightness = getPreferredBrightness(); - - if ((difference & KEYBOARD_BRIGHT_BIT) != 0) { - if ((newState & KEYBOARD_BRIGHT_BIT) == 0) { - offMask |= KEYBOARD_BRIGHT_BIT; - } else { - onMask |= KEYBOARD_BRIGHT_BIT; - } - } - - if ((difference & BUTTON_BRIGHT_BIT) != 0) { - if ((newState & BUTTON_BRIGHT_BIT) == 0) { - offMask |= BUTTON_BRIGHT_BIT; - } else { - onMask |= BUTTON_BRIGHT_BIT; - } - } - - if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) { - int nominalCurrentValue = -1; - // If there was an actual difference in the light state, then - // figure out the "ideal" current value based on the previous - // state. Otherwise, this is a change due to the brightness - // override, so we want to animate from whatever the current - // value is. - if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) { - switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) { - case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT: - nominalCurrentValue = preferredBrightness; - break; - case SCREEN_ON_BIT: - nominalCurrentValue = mScreenBrightnessDim; - break; - case 0: - nominalCurrentValue = PowerManager.BRIGHTNESS_OFF; - break; - case SCREEN_BRIGHT_BIT: - default: - // not possible - nominalCurrentValue = (int)mScreenBrightnessAnimator.getCurrentBrightness(); - break; - } - } - int brightness = preferredBrightness; - int steps = ANIM_STEPS; - if ((newState & SCREEN_BRIGHT_BIT) == 0) { - // dim or turn off backlight, depending on if the screen is on - // the scale is because the brightness ramp isn't linear and this biases - // it so the later parts take longer. - final float scale = 1.5f; - float ratio = (((float)mScreenBrightnessDim)/preferredBrightness); - if (ratio > 1.0f) ratio = 1.0f; - if ((newState & SCREEN_ON_BIT) == 0) { - if ((oldState & SCREEN_BRIGHT_BIT) != 0) { - // was bright - steps = ANIM_STEPS; - } else { - // was dim - steps = (int)(ANIM_STEPS*ratio*scale); - } - brightness = PowerManager.BRIGHTNESS_OFF; - } else { - if ((oldState & SCREEN_ON_BIT) != 0) { - // was bright - steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale); - } else { - // was dim - steps = (int)(ANIM_STEPS*ratio); - } - final int stayOnConditions = getStayOnConditionsLocked(); - if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) { - // If the "stay on while plugged in" option is - // turned on, then the screen will often not - // automatically turn off while plugged in. To - // still have a sense of when it is inactive, we - // will then count going dim as turning off. - mScreenOffTime = SystemClock.elapsedRealtime(); - } - brightness = mScreenBrightnessDim; - } - } - if (mWaitingForFirstLightSensor && (newState & SCREEN_ON_BIT) != 0) { - steps = IMMEDIATE_ANIM_STEPS; - } - - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(brightness); - } catch (RemoteException e) { - // Nothing interesting to do. - } finally { - Binder.restoreCallingIdentity(identity); - } - if (!mSkippedScreenOn) { - int dt = steps * NOMINAL_FRAME_TIME_MS; - mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, dt); - if (DEBUG_SCREEN_ON) { - RuntimeException e = new RuntimeException("here"); - e.fillInStackTrace(); - Slog.i(TAG, "Setting screen brightness: " + brightness, e); - } - } - } - - if (mSpew) { - Slog.d(TAG, "offMask=0x" + Integer.toHexString(offMask) - + " dimMask=0x" + Integer.toHexString(dimMask) - + " onMask=0x" + Integer.toHexString(onMask) - + " difference=0x" + Integer.toHexString(difference) - + " realDifference=0x" + Integer.toHexString(realDifference) - + " forceState=0x" + Integer.toHexString(forceState) - ); - } - - if (offMask != 0) { - if (mSpew) Slog.i(TAG, "Setting brightess off: " + offMask); - setLightBrightness(offMask, PowerManager.BRIGHTNESS_OFF); - } - if (dimMask != 0) { - int brightness = mScreenBrightnessDim; - if ((newState & BATTERY_LOW_BIT) != 0 && - brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) { - brightness = PowerManager.BRIGHTNESS_LOW_BATTERY; - } - if (mSpew) Slog.i(TAG, "Setting brightess dim " + brightness + ": " + dimMask); - setLightBrightness(dimMask, brightness); - } - if (onMask != 0) { - int brightness = getPreferredBrightness(); - if ((newState & BATTERY_LOW_BIT) != 0 && - brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) { - brightness = PowerManager.BRIGHTNESS_LOW_BATTERY; - } - if (mSpew) Slog.i(TAG, "Setting brightess on " + brightness + ": " + onMask); - setLightBrightness(onMask, brightness); - } - } - - /** - * Note: by design this class does not hold mLocks while calling native methods. - * Nor should it. Ever. - */ - class ScreenBrightnessAnimator extends HandlerThread { - static final int ANIMATE_LIGHTS = 10; - static final int ANIMATE_POWER_OFF = 11; - volatile int startValue; - volatile int endValue; - volatile int startSensorValue; - volatile int endSensorValue; - volatile int currentValue; - private int currentMask; - private int duration; - private long startTimeMillis; - private final String prefix; - - public ScreenBrightnessAnimator(String name, int priority) { - super(name, priority); - prefix = name; - } - - @Override - protected void onLooperPrepared() { - mScreenBrightnessHandler = new Handler() { - public void handleMessage(Message msg) { - int brightnessMode = (mAutoBrightessEnabled && !mInitialAnimation - ? LightsService.BRIGHTNESS_MODE_SENSOR - : LightsService.BRIGHTNESS_MODE_USER); - if (msg.what == ANIMATE_LIGHTS) { - final int mask = msg.arg1; - int value = msg.arg2; - long tStart = SystemClock.uptimeMillis(); - if ((mask & SCREEN_BRIGHT_BIT) != 0) { - if (mDebugLightAnimation) Slog.v(TAG, "Set brightness: " + value); - mLcdLight.setBrightness(value, brightnessMode); - } - long elapsed = SystemClock.uptimeMillis() - tStart; - if ((mask & BUTTON_BRIGHT_BIT) != 0) { - mButtonLight.setBrightness(value); - } - if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { - mKeyboardLight.setBrightness(value); - } - - if (elapsed > 100) { - Slog.e(TAG, "Excessive delay setting brightness: " + elapsed - + "ms, mask=" + mask); - } - - // Throttle brightness updates to frame refresh rate - int delay = elapsed < NOMINAL_FRAME_TIME_MS ? NOMINAL_FRAME_TIME_MS : 1; - synchronized(this) { - currentValue = value; - } - animateInternal(mask, false, delay); - } else if (msg.what == ANIMATE_POWER_OFF) { - int mode = msg.arg1; - nativeStartSurfaceFlingerAnimation(mode); - } - } - }; - synchronized (this) { - mInitComplete = true; - notifyAll(); - } - } - - private void animateInternal(int mask, boolean turningOff, int delay) { - synchronized (this) { - if (currentValue != endValue) { - final long now = SystemClock.elapsedRealtime(); - final int elapsed = (int) (now - startTimeMillis); - int newValue; - if (elapsed < duration) { - int delta = endValue - startValue; - newValue = startValue + delta * elapsed / duration; - newValue = Math.max(PowerManager.BRIGHTNESS_OFF, newValue); - newValue = Math.min(PowerManager.BRIGHTNESS_ON, newValue); - // Optimization to delay next step until a change will occur. - if (delay > 0 && newValue == currentValue) { - final int timePerStep = duration / Math.abs(delta); - delay = Math.min(duration - elapsed, timePerStep); - newValue += delta < 0 ? -1 : 1; - } - // adjust the peak sensor value until we get to the target sensor value - delta = endSensorValue - startSensorValue; - mHighestLightSensorValue = startSensorValue + delta * elapsed / duration; - } else { - newValue = endValue; - mHighestLightSensorValue = endSensorValue; - if (endValue > 0) { - mInitialAnimation = false; - } - } - - if (mDebugLightAnimation) { - Slog.v(TAG, "Animating light: " + "start:" + startValue - + ", end:" + endValue + ", elapsed:" + elapsed - + ", duration:" + duration + ", current:" + currentValue - + ", newValue:" + newValue - + ", delay:" + delay - + ", highestSensor:" + mHighestLightSensorValue); - } - - if (turningOff && !mHeadless && !mAnimateScreenLights) { - int mode = mScreenOffReason == OFF_BECAUSE_OF_PROX_SENSOR - ? 0 : mAnimationSetting; - if (mDebugLightAnimation) { - Slog.v(TAG, "Doing power-off anim, mode=" + mode); - } - mScreenBrightnessHandler.obtainMessage(ANIMATE_POWER_OFF, mode, 0) - .sendToTarget(); - } - mScreenBrightnessHandler.removeMessages( - ScreenBrightnessAnimator.ANIMATE_LIGHTS); - Message msg = mScreenBrightnessHandler - .obtainMessage(ANIMATE_LIGHTS, mask, newValue); - mScreenBrightnessHandler.sendMessageDelayed(msg, delay); - } - } - } - - public void dump(PrintWriter pw, String string) { - pw.println(string); - pw.println(" animating: " + "start:" + startValue + ", end:" + endValue - + ", duration:" + duration + ", current:" + currentValue); - pw.println(" startSensorValue:" + startSensorValue - + " endSensorValue:" + endSensorValue); - pw.println(" startTimeMillis:" + startTimeMillis - + " now:" + SystemClock.elapsedRealtime()); - pw.println(" currentMask:" + dumpPowerState(currentMask)); - } - - public void animateTo(int target, int mask, int animationDuration) { - animateTo(target, mHighestLightSensorValue, mask, animationDuration); - } - - public void animateTo(int target, int sensorTarget, int mask, int animationDuration) { - synchronized(this) { - if ((mask & SCREEN_BRIGHT_BIT) == 0) { - // We only animate keyboard and button when passed in with SCREEN_BRIGHT_BIT. - if ((mask & BUTTON_BRIGHT_BIT) != 0) { - mButtonLight.setBrightness(target); - } - if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { - mKeyboardLight.setBrightness(target); - } - return; - } - if (isAnimating() && (mask ^ currentMask) != 0) { - // current animation is unrelated to new animation, jump to final values - cancelAnimation(); - } - if (mInitialAnimation) { - // jump to final value in one step the first time the brightness is set - animationDuration = 0; - if (target > 0) { - mInitialAnimation = false; - } - } - startValue = currentValue; - endValue = target; - startSensorValue = mHighestLightSensorValue; - endSensorValue = sensorTarget; - currentMask = mask; - duration = (int) (mWindowScaleAnimation * animationDuration); - startTimeMillis = SystemClock.elapsedRealtime(); - - if (mDebugLightAnimation) { - Slog.v(TAG, "animateTo(target=" + target - + ", sensor=" + sensorTarget - + ", mask=" + mask - + ", duration=" + animationDuration +")" - + ", currentValue=" + currentValue - + ", startTime=" + startTimeMillis); - } - - if (target != currentValue) { - final boolean doScreenAnim = (mask & (SCREEN_BRIGHT_BIT | SCREEN_ON_BIT)) != 0; - final boolean turningOff = endValue == PowerManager.BRIGHTNESS_OFF; - if (turningOff && doScreenAnim) { - // Cancel all pending animations since we're turning off - mScreenBrightnessHandler.removeCallbacksAndMessages(null); - screenOffFinishedAnimatingLocked(mScreenOffReason); - duration = 200; // TODO: how long should this be? - } - if (doScreenAnim) { - animateInternal(mask, turningOff, 0); - } - // TODO: Handle keyboard light animation when we have devices that support it - } - } - } - - public int getCurrentBrightness() { - synchronized (this) { - return currentValue; - } - } - - public boolean isAnimating() { - synchronized (this) { - return currentValue != endValue; - } - } - - public void cancelAnimation() { - animateTo(endValue, currentMask, 0); - } - } - - private void setLightBrightness(int mask, int value) { - mScreenBrightnessAnimator.animateTo(value, mask, 0); - } - - private int getPreferredBrightness() { - if (mScreenBrightnessOverride >= 0) { - return mScreenBrightnessOverride; - } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness - && mAutoBrightessEnabled) { - return mLightSensorScreenBrightness; - } - final int brightness = mScreenBrightnessSetting; - // Don't let applications turn the screen all the way off - return Math.max(brightness, mScreenBrightnessDim); - } - - private int applyButtonState(int state) { - int brightness = -1; - if ((state & BATTERY_LOW_BIT) != 0) { - // do not override brightness if the battery is low - return state; - } - if (mButtonBrightnessOverride >= 0) { - brightness = mButtonBrightnessOverride; - } else if (mLightSensorButtonBrightness >= 0 && mUseSoftwareAutoBrightness) { - brightness = mLightSensorButtonBrightness; - } - if (brightness > 0) { - return state | BUTTON_BRIGHT_BIT; - } else if (brightness == 0) { - return state & ~BUTTON_BRIGHT_BIT; - } else { - return state; - } - } - - private int applyKeyboardState(int state) { - int brightness = -1; - if ((state & BATTERY_LOW_BIT) != 0) { - // do not override brightness if the battery is low - return state; - } - if (!mKeyboardVisible) { - brightness = 0; - } else if (mButtonBrightnessOverride >= 0) { - brightness = mButtonBrightnessOverride; - } else if (mLightSensorKeyboardBrightness >= 0 && mUseSoftwareAutoBrightness) { - brightness = mLightSensorKeyboardBrightness; - } - if (brightness > 0) { - return state | KEYBOARD_BRIGHT_BIT; - } else if (brightness == 0) { - return state & ~KEYBOARD_BRIGHT_BIT; - } else { - return state; - } - } - - public boolean isScreenOn() { - synchronized (mLocks) { - return (mPowerState & SCREEN_ON_BIT) != 0; - } - } - - boolean isScreenBright() { - synchronized (mLocks) { - return (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT; - } - } - - private boolean isScreenTurningOffLocked() { - return (mScreenBrightnessAnimator.isAnimating() - && mScreenBrightnessAnimator.endValue == PowerManager.BRIGHTNESS_OFF - && (mScreenBrightnessAnimator.currentMask & SCREEN_BRIGHT_BIT) != 0); - } - - private boolean shouldLog(long time) { - synchronized (mLocks) { - if (time > (mWarningSpewThrottleTime + (60*60*1000))) { - mWarningSpewThrottleTime = time; - mWarningSpewThrottleCount = 0; - return true; - } else if (mWarningSpewThrottleCount < 30) { - mWarningSpewThrottleCount++; - return true; - } else { - return false; - } - } - } - - private void forceUserActivityLocked() { - if (isScreenTurningOffLocked()) { - // cancel animation so userActivity will succeed - mScreenBrightnessAnimator.cancelAnimation(); - } - boolean savedActivityAllowed = mUserActivityAllowed; - mUserActivityAllowed = true; - userActivity(SystemClock.uptimeMillis(), false); - mUserActivityAllowed = savedActivityAllowed; - } - - public void userActivityWithForce(long time, boolean noChangeLights, boolean force) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - userActivity(time, -1, noChangeLights, OTHER_EVENT, force, false); - } - - public void userActivity(long time, boolean noChangeLights) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER) - != PackageManager.PERMISSION_GRANTED) { - if (shouldLog(time)) { - Slog.w(TAG, "Caller does not have DEVICE_POWER permission. pid=" - + Binder.getCallingPid() + " uid=" + Binder.getCallingUid()); - } - return; - } - - userActivity(time, -1, noChangeLights, OTHER_EVENT, false, false); - } - - public void userActivity(long time, boolean noChangeLights, int eventType) { - userActivity(time, -1, noChangeLights, eventType, false, false); - } - - public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) { - userActivity(time, -1, noChangeLights, eventType, force, false); - } - - /* - * Reset the user activity timeout to now + timeout. This overrides whatever else is going - * on with user activity. Don't use this function. - */ - public void clearUserActivityTimeout(long now, long timeout) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now"); - userActivity(now, timeout, false, OTHER_EVENT, false, false); - } - - private void userActivity(long time, long timeoutOverride, boolean noChangeLights, - int eventType, boolean force, boolean ignoreIfScreenOff) { - - if (((mPokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0) && (eventType == TOUCH_EVENT)) { - if (false) { - Slog.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey)); - } - return; - } - - synchronized (mLocks) { - if (mSpew) { - Slog.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time - + " mUserActivityAllowed=" + mUserActivityAllowed - + " mUserState=0x" + Integer.toHexString(mUserState) - + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState) - + " mProximitySensorActive=" + mProximitySensorActive - + " timeoutOverride=" + timeoutOverride - + " force=" + force); - } - // ignore user activity if we are in the process of turning off the screen - if (isScreenTurningOffLocked()) { - Slog.d(TAG, "ignoring user activity while turning off screen"); - return; - } - // ignore if the caller doesn't want this to allow the screen to turn - // on, and the screen is currently off. - if (ignoreIfScreenOff && (mPowerState & SCREEN_ON_BIT) == 0) { - return; - } - // Disable proximity sensor if if user presses power key while we are in the - // "waiting for proximity sensor to go negative" state. - if (mProximitySensorActive && mProximityWakeLockCount == 0) { - mProximitySensorActive = false; - } - if (mLastEventTime <= time || force) { - mLastEventTime = time; - if ((mUserActivityAllowed && !mProximitySensorActive) || force) { - // Only turn on button backlights if a button was pressed - // and auto brightness is disabled - if (eventType == BUTTON_EVENT && !mUseSoftwareAutoBrightness) { - mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT); - } else { - // don't clear button/keyboard backlights when the screen is touched. - mUserState |= SCREEN_BRIGHT; - } - - int uid = Binder.getCallingUid(); - long ident = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteUserActivity(uid, eventType); - } catch (RemoteException e) { - // Ignore - } finally { - Binder.restoreCallingIdentity(ident); - } - - mWakeLockState = mLocks.reactivateScreenLocksLocked(); - setPowerState(mUserState | mWakeLockState, noChangeLights, - WindowManagerPolicy.OFF_BECAUSE_OF_USER); - setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT); - } - } - } - - if (mPolicy != null) { - mPolicy.userActivity(); - } - } - - private int getAutoBrightnessValue(int sensorValue, int[] values) { - try { - int i; - for (i = 0; i < mAutoBrightnessLevels.length; i++) { - if (sensorValue < mAutoBrightnessLevels[i]) { - break; - } - } - // This is the range of brightness values that we can use. - final int minval = values[0]; - final int maxval = values[mAutoBrightnessLevels.length]; - // This is the range we will be scaling. We put some padding - // at the low and high end to give the adjustment a little better - // impact on the actual observed value. - final int range = (maxval-minval) + LIGHT_SENSOR_RANGE_EXPANSION; - // This is the desired brightness value from 0.0 to 1.0. - float valf = ((values[i]-minval+(LIGHT_SENSOR_RANGE_EXPANSION/2))/(float)range); - // Apply a scaling to the value based on the adjustment. - if (mLightSensorAdjustSetting > 0 && mLightSensorAdjustSetting <= 1) { - float adj = (float)Math.sqrt(1.0f-mLightSensorAdjustSetting); - if (adj <= .00001) { - valf = 1; - } else { - valf /= adj; - } - } else if (mLightSensorAdjustSetting < 0 && mLightSensorAdjustSetting >= -1) { - float adj = (float)Math.sqrt(1.0f+mLightSensorAdjustSetting); - valf *= adj; - } - // Apply an additional offset to the value based on the adjustment. - valf += mLightSensorAdjustSetting/LIGHT_SENSOR_OFFSET_SCALE; - // Convert the 0.0-1.0 value back to a brightness integer. - int val = (int)((valf*range)+minval) - (LIGHT_SENSOR_RANGE_EXPANSION/2); - if (val < minval) val = minval; - else if (val > maxval) val = maxval; - return val; - } catch (Exception e) { - // guard against null pointer or index out of bounds errors - Slog.e(TAG, "Values array must be non-empty and must be one element longer than " - + "the auto-brightness levels array. Check config.xml.", e); - return 255; - } - } - - private Runnable mProximityTask = new Runnable() { - public void run() { - synchronized (mLocks) { - if (mProximityPendingValue != -1) { - proximityChangedLocked(mProximityPendingValue == 1); - mProximityPendingValue = -1; - } - if (mProximityPartialLock.isHeld()) { - mProximityPartialLock.release(); - } - } - } - }; - - private Runnable mAutoBrightnessTask = new Runnable() { - public void run() { - synchronized (mLocks) { - if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) { - int value = (int)mLightSensorPendingValue; - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - lightSensorChangedLocked(value, false); - } - } - } - }; - - /** used to prevent lightsensor changes while turning on. */ - private boolean mInitialAnimation = true; - - private void dockStateChanged(int state) { - synchronized (mLocks) { - mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED); - if (mIsDocked) { - // allow brightness to decrease when docked - mHighestLightSensorValue = -1; - } - if ((mPowerState & SCREEN_ON_BIT) != 0) { - // force lights recalculation - int value = (int)mLightSensorValue; - mLightSensorValue = -1; - lightSensorChangedLocked(value, false); - } - } - } - - private void lightSensorChangedLocked(int value, boolean immediate) { - if (mDebugLightSensor) { - Slog.d(TAG, "lightSensorChangedLocked value=" + value + " immediate=" + immediate); - } - - // Don't do anything if the screen is off. - if ((mPowerState & SCREEN_ON_BIT) == 0) { - if (mDebugLightSensor) { - Slog.d(TAG, "dropping lightSensorChangedLocked because screen is off"); - } - return; - } - - if (mLightSensorValue != value) { - mLightSensorValue = value; - if ((mPowerState & BATTERY_LOW_BIT) == 0) { - // use maximum light sensor value seen since screen went on for LCD to avoid flicker - // we only do this if we are undocked, since lighting should be stable when - // stationary in a dock. - int lcdValue = getAutoBrightnessValue(value, mLcdBacklightValues); - int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues); - int keyboardValue; - if (mKeyboardVisible) { - keyboardValue = getAutoBrightnessValue(value, mKeyboardBacklightValues); - } else { - keyboardValue = 0; - } - mLightSensorScreenBrightness = lcdValue; - mLightSensorButtonBrightness = buttonValue; - mLightSensorKeyboardBrightness = keyboardValue; - - if (mDebugLightSensor) { - Slog.d(TAG, "lcdValue " + lcdValue); - Slog.d(TAG, "buttonValue " + buttonValue); - Slog.d(TAG, "keyboardValue " + keyboardValue); - } - - if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) { - if (!mSkippedScreenOn && !mInitialAnimation) { - final int steps; - if (immediate) { - steps = IMMEDIATE_ANIM_STEPS; - } else { - synchronized (mScreenBrightnessAnimator) { - if (mScreenBrightnessAnimator.currentValue <= lcdValue) { - steps = AUTOBRIGHTNESS_ANIM_STEPS; - } else { - steps = AUTODIMNESS_ANIM_STEPS; - } - } - } - mScreenBrightnessAnimator.animateTo(lcdValue, value, - SCREEN_BRIGHT_BIT, steps * NOMINAL_FRAME_TIME_MS); - } - } - if (mButtonBrightnessOverride < 0) { - mButtonLight.setBrightness(buttonValue); - } - if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) { - mKeyboardLight.setBrightness(keyboardValue); - } - } - } - } - - /** - * The user requested that we go to sleep (probably with the power button). - * This overrides all wake locks that are held. - */ - public void goToSleep(long time) - { - goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER); - } - - /** - * The user requested that we go to sleep (probably with the power button). - * This overrides all wake locks that are held. - */ - public void goToSleepWithReason(long time, int reason) - { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - synchronized (mLocks) { - goToSleepLocked(time, reason); - } - } - - /** - * Reboot the device immediately, passing 'reason' (may be null) - * to the underlying __reboot system call. Should not return. - */ - public void reboot(String reason) - { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); - - if (mHandler == null || !ActivityManagerNative.isSystemReady()) { - throw new IllegalStateException("Too early to call reboot()"); - } - - final String finalReason = reason; - Runnable runnable = new Runnable() { - public void run() { - synchronized (this) { - ShutdownThread.reboot(mContext, finalReason, false); - } - - } - }; - // ShutdownThread must run on a looper capable of displaying the UI. - mHandler.post(runnable); - - // PowerManager.reboot() is documented not to return so just wait for the inevitable. - synchronized (runnable) { - while (true) { - try { - runnable.wait(); - } catch (InterruptedException e) { - } - } - } - } - - /** - * Crash the runtime (causing a complete restart of the Android framework). - * Requires REBOOT permission. Mostly for testing. Should not return. - */ - public void crash(final String message) - { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); - Thread t = new Thread("PowerManagerService.crash()") { - public void run() { throw new RuntimeException(message); } - }; - try { - t.start(); - t.join(); - } catch (InterruptedException e) { - Log.wtf(TAG, e); - } - } - - private void goToSleepLocked(long time, int reason) { - if (mSpew) { - Exception ex = new Exception(); - ex.fillInStackTrace(); - Slog.d(TAG, "goToSleep mLastEventTime=" + mLastEventTime + " time=" + time - + " reason=" + reason, ex); - } - - if (mLastEventTime <= time) { - mLastEventTime = time; - // cancel all of the wake locks - mWakeLockState = SCREEN_OFF; - int N = mLocks.size(); - int numCleared = 0; - boolean proxLock = false; - for (int i=0; i<N; i++) { - WakeLock wl = mLocks.get(i); - if (isScreenLock(wl.flags)) { - if (((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) - && reason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) { - proxLock = true; - } else { - mLocks.get(i).activated = false; - numCleared++; - } - } - } - if (!proxLock) { - mProxIgnoredBecauseScreenTurnedOff = true; - if (mDebugProximitySensor) { - Slog.d(TAG, "setting mProxIgnoredBecauseScreenTurnedOff"); - } - } - EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numCleared); - mStillNeedSleepNotification = true; - mUserState = SCREEN_OFF; - setPowerState(SCREEN_OFF, false, reason); - cancelTimerLocked(); - } - } - - public long timeSinceScreenOn() { - synchronized (mLocks) { - if ((mPowerState & SCREEN_ON_BIT) != 0) { - return 0; - } - return SystemClock.elapsedRealtime() - mScreenOffTime; - } - } - - public void setKeyboardVisibility(boolean visible) { - synchronized (mLocks) { - if (mSpew) { - Slog.d(TAG, "setKeyboardVisibility: " + visible); - } - if (mKeyboardVisible != visible) { - mKeyboardVisible = visible; - // don't signal user activity if the screen is off; other code - // will take care of turning on due to a true change to the lid - // switch and synchronized with the lock screen. - if ((mPowerState & SCREEN_ON_BIT) != 0) { - if (mUseSoftwareAutoBrightness) { - // force recompute of backlight values - if (mLightSensorValue >= 0) { - int value = (int)mLightSensorValue; - mLightSensorValue = -1; - lightSensorChangedLocked(value, false); - } - } - userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true); - } - } - } - } - - /** - * When the keyguard is up, it manages the power state, and userActivity doesn't do anything. - * When disabling user activity we also reset user power state so the keyguard can reset its - * short screen timeout when keyguard is unhidden. - */ - public void enableUserActivity(boolean enabled) { - if (mSpew) { - Slog.d(TAG, "enableUserActivity " + enabled); - } - synchronized (mLocks) { - mUserActivityAllowed = enabled; - if (!enabled) { - // cancel timeout and clear mUserState so the keyguard can set a short timeout - setTimeoutLocked(SystemClock.uptimeMillis(), 0); - } - } - } - - private void setScreenBrightnessMode(int mode) { - synchronized (mLocks) { - boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) { - mAutoBrightessEnabled = enabled; - // This will get us a new value - enableLightSensorLocked(mAutoBrightessEnabled && isScreenOn()); - } - } - } - - /** Sets the screen off timeouts: - * mKeylightDelay - * mDimDelay - * mScreenOffDelay - * */ - private void setScreenOffTimeoutsLocked() { - if ((mPokey & POKE_LOCK_SHORT_TIMEOUT) != 0) { - mKeylightDelay = mShortKeylightDelay; // Configurable via secure settings - mDimDelay = -1; - mScreenOffDelay = 0; - } else if ((mPokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0) { - mKeylightDelay = MEDIUM_KEYLIGHT_DELAY; - mDimDelay = -1; - mScreenOffDelay = 0; - } else { - int totalDelay = mScreenOffTimeoutSetting; - if (totalDelay > mMaximumScreenOffTimeout) { - totalDelay = mMaximumScreenOffTimeout; - } - mKeylightDelay = LONG_KEYLIGHT_DELAY; - if (totalDelay < 0) { - // negative number means stay on as long as possible. - mScreenOffDelay = mMaximumScreenOffTimeout; - } else if (mKeylightDelay < totalDelay) { - // subtract the time that the keylight delay. This will give us the - // remainder of the time that we need to sleep to get the accurate - // screen off timeout. - mScreenOffDelay = totalDelay - mKeylightDelay; - } else { - mScreenOffDelay = 0; - } - if (mDimScreen && totalDelay >= (LONG_KEYLIGHT_DELAY + LONG_DIM_TIME)) { - mDimDelay = mScreenOffDelay - LONG_DIM_TIME; - mScreenOffDelay = LONG_DIM_TIME; - } else { - mDimDelay = -1; - } - } - if (mSpew) { - Slog.d(TAG, "setScreenOffTimeouts mKeylightDelay=" + mKeylightDelay - + " mDimDelay=" + mDimDelay + " mScreenOffDelay=" + mScreenOffDelay - + " mDimScreen=" + mDimScreen); - } - } - - /** - * Refreshes cached secure settings. Called once on startup, and - * on subsequent changes to secure settings. - */ - private void updateSettingsValues() { - mShortKeylightDelay = Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.SHORT_KEYLIGHT_DELAY_MS, - SHORT_KEYLIGHT_DELAY_DEFAULT); - // Slog.i(TAG, "updateSettingsValues(): mShortKeylightDelay now " + mShortKeylightDelay); - } - - private class LockList extends ArrayList<WakeLock> - { - void addLock(WakeLock wl) - { - int index = getIndex(wl.binder); - if (index < 0) { - this.add(wl); - } - } - - WakeLock removeLock(IBinder binder) - { - int index = getIndex(binder); - if (index >= 0) { - return this.remove(index); - } else { - return null; - } - } - - int getIndex(IBinder binder) - { - int N = this.size(); - for (int i=0; i<N; i++) { - if (this.get(i).binder == binder) { - return i; - } - } - return -1; - } - - int gatherState() - { - int result = 0; - int N = this.size(); - for (int i=0; i<N; i++) { - WakeLock wl = this.get(i); - if (wl.activated) { - if (isScreenLock(wl.flags)) { - result |= wl.minState; - } - } - } - return result; - } - - int reactivateScreenLocksLocked() - { - int result = 0; - int N = this.size(); - for (int i=0; i<N; i++) { - WakeLock wl = this.get(i); - if (isScreenLock(wl.flags)) { - wl.activated = true; - result |= wl.minState; - } - } - if (mDebugProximitySensor) { - Slog.d(TAG, "reactivateScreenLocksLocked mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); - } - mProxIgnoredBecauseScreenTurnedOff = false; - return result; - } - } - - public void setPolicy(WindowManagerPolicy p) { - synchronized (mLocks) { - mPolicy = p; - mLocks.notifyAll(); - } - } - - WindowManagerPolicy getPolicyLocked() { - while (mPolicy == null || !mDoneBooting) { - try { - mLocks.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - return mPolicy; - } - - void systemReady() { - mSensorManager = new SystemSensorManager(mHandlerThread.getLooper()); - mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); - // don't bother with the light sensor if auto brightness is handled in hardware - if (mUseSoftwareAutoBrightness) { - mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); - } - - // wait until sensors are enabled before turning on screen. - // some devices will not activate the light sensor properly on boot - // unless we do this. - if (mUseSoftwareAutoBrightness) { - // turn the screen on - setPowerState(SCREEN_BRIGHT); - } else { - // turn everything on - setPowerState(ALL_BRIGHT); - } - - synchronized (mLocks) { - Slog.d(TAG, "system ready!"); - mDoneBooting = true; - - enableLightSensorLocked(mUseSoftwareAutoBrightness && mAutoBrightessEnabled); - - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(getPreferredBrightness()); - mBatteryStats.noteScreenOn(); - } catch (RemoteException e) { - // Nothing interesting to do. - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - void bootCompleted() { - Slog.d(TAG, "bootCompleted"); - synchronized (mLocks) { - mBootCompleted = true; - userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true); - updateWakeLockLocked(); - mLocks.notifyAll(); - } - } - - // for watchdog - public void monitor() { - synchronized (mLocks) { } - } - - public int getSupportedWakeLockFlags() { - int result = PowerManager.PARTIAL_WAKE_LOCK - | PowerManager.FULL_WAKE_LOCK - | PowerManager.SCREEN_DIM_WAKE_LOCK; - - if (mProximitySensor != null) { - result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK; - } - - return result; - } - - public void setBacklightBrightness(int brightness) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - // Don't let applications turn the screen all the way off - synchronized (mLocks) { - brightness = Math.max(brightness, mScreenBrightnessDim); - mLcdLight.setBrightness(brightness); - mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0); - mButtonLight.setBrightness(brightness); - long identity = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteScreenBrightness(brightness); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, 0); - } - } - - public void setAutoBrightnessAdjustment(float adj) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - synchronized (mLocks) { - mLightSensorAdjustSetting = adj; - if (mSensorManager != null && mLightSensorEnabled) { - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - // force recompute of backlight values - if (mLightSensorValue >= 0) { - int value = (int)mLightSensorValue; - mLightSensorValue = -1; - handleLightSensorValue(value, true); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - } - - public void setAttentionLight(boolean on, int color) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - mAttentionLight.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); - } - - private void enableProximityLockLocked() { - if (mDebugProximitySensor) { - Slog.d(TAG, "enableProximityLockLocked"); - } - if (!mProximitySensorEnabled) { - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - mSensorManager.registerListener(mProximityListener, mProximitySensor, - SensorManager.SENSOR_DELAY_NORMAL); - mProximitySensorEnabled = true; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - private void disableProximityLockLocked() { - if (mDebugProximitySensor) { - Slog.d(TAG, "disableProximityLockLocked"); - } - if (mProximitySensorEnabled) { - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - mSensorManager.unregisterListener(mProximityListener); - mHandler.removeCallbacks(mProximityTask); - if (mProximityPartialLock.isHeld()) { - mProximityPartialLock.release(); - } - mProximitySensorEnabled = false; - } finally { - Binder.restoreCallingIdentity(identity); - } - if (mProximitySensorActive) { - mProximitySensorActive = false; - if (mDebugProximitySensor) { - Slog.d(TAG, "disableProximityLockLocked mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); - } - if (!mProxIgnoredBecauseScreenTurnedOff) { - forceUserActivityLocked(); - } - } - } - } - - private void proximityChangedLocked(boolean active) { - if (mDebugProximitySensor) { - Slog.d(TAG, "proximityChangedLocked, active: " + active); - } - if (!mProximitySensorEnabled) { - Slog.d(TAG, "Ignoring proximity change after sensor is disabled"); - return; - } - if (active) { - if (mDebugProximitySensor) { - Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); - } - if (!mProxIgnoredBecauseScreenTurnedOff) { - goToSleepLocked(SystemClock.uptimeMillis(), - WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR); - } - mProximitySensorActive = true; - } else { - // proximity sensor negative events trigger as user activity. - // temporarily set mUserActivityAllowed to true so this will work - // even when the keyguard is on. - mProximitySensorActive = false; - if (mDebugProximitySensor) { - Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff=" - + mProxIgnoredBecauseScreenTurnedOff); - } - if (!mProxIgnoredBecauseScreenTurnedOff) { - forceUserActivityLocked(); - } - - if (mProximityWakeLockCount == 0) { - // disable sensor if we have no listeners left after proximity negative - disableProximityLockLocked(); - } - } - } - - private void enableLightSensorLocked(boolean enable) { - if (mDebugLightSensor) { - Slog.d(TAG, "enableLightSensorLocked enable=" + enable - + " mLightSensorEnabled=" + mLightSensorEnabled - + " mAutoBrightessEnabled=" + mAutoBrightessEnabled - + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor); - } - if (!mAutoBrightessEnabled) { - enable = false; - } - if (mSensorManager != null && mLightSensorEnabled != enable) { - mLightSensorEnabled = enable; - // clear calling identity so sensor manager battery stats are accurate - long identity = Binder.clearCallingIdentity(); - try { - if (enable) { - // reset our highest value when reenabling - mHighestLightSensorValue = -1; - // force recompute of backlight values - final int value = (int)mLightSensorValue; - if (value >= 0) { - mLightSensorValue = -1; - handleLightSensorValue(value, true); - } - mSensorManager.registerListener(mLightListener, mLightSensor, - LIGHT_SENSOR_RATE); - } else { - mSensorManager.unregisterListener(mLightListener); - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - SensorEventListener mProximityListener = new SensorEventListener() { - public void onSensorChanged(SensorEvent event) { - long milliseconds = SystemClock.elapsedRealtime(); - synchronized (mLocks) { - float distance = event.values[0]; - long timeSinceLastEvent = milliseconds - mLastProximityEventTime; - mLastProximityEventTime = milliseconds; - mHandler.removeCallbacks(mProximityTask); - boolean proximityTaskQueued = false; - - // compare against getMaximumRange to support sensors that only return 0 or 1 - boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD && - distance < mProximitySensor.getMaximumRange()); - - if (mDebugProximitySensor) { - Slog.d(TAG, "mProximityListener.onSensorChanged active: " + active); - } - if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) { - // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing - mProximityPendingValue = (active ? 1 : 0); - mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent); - proximityTaskQueued = true; - } else { - // process the value immediately - mProximityPendingValue = -1; - proximityChangedLocked(active); - } - - // update mProximityPartialLock state - boolean held = mProximityPartialLock.isHeld(); - if (!held && proximityTaskQueued) { - // hold wakelock until mProximityTask runs - mProximityPartialLock.acquire(); - } else if (held && !proximityTaskQueued) { - mProximityPartialLock.release(); - } - } - } - - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // ignore - } - }; - - private void handleLightSensorValue(int value, boolean immediate) { - long milliseconds = SystemClock.elapsedRealtime(); - if (mLightSensorValue == -1 - || milliseconds < mLastScreenOnTime + mLightSensorWarmupTime - || mWaitingForFirstLightSensor) { - // process the value immediately if screen has just turned on - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = false; - mLightSensorPendingIncrease = false; - lightSensorChangedLocked(value, immediate); - } else { - if ((value > mLightSensorValue && mLightSensorPendingDecrease) || - (value < mLightSensorValue && mLightSensorPendingIncrease) || - (value == mLightSensorValue) || - (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) { - // delay processing to debounce the sensor - mHandler.removeCallbacks(mAutoBrightnessTask); - mLightSensorPendingDecrease = (value < mLightSensorValue); - mLightSensorPendingIncrease = (value > mLightSensorValue); - if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) { - mLightSensorPendingValue = value; - mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY); - } - } else { - mLightSensorPendingValue = value; - } - } - } - - SensorEventListener mLightListener = new SensorEventListener() { - @Override - public void onSensorChanged(SensorEvent event) { - if (mDebugLightSensor) { - Slog.d(TAG, "onSensorChanged: light value: " + event.values[0]); - } - synchronized (mLocks) { - // ignore light sensor while screen is turning off - if (isScreenTurningOffLocked()) { - return; - } - handleLightSensorValue((int)event.values[0], mWaitingForFirstLightSensor); - if (mWaitingForFirstLightSensor && !mPreparingForScreenOn) { - if (mDebugLightAnimation) { - Slog.d(TAG, "onSensorChanged: Clearing mWaitingForFirstLightSensor."); - } - mWaitingForFirstLightSensor = false; - } - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // ignore - } - }; -} diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java new file mode 100644 index 0000000..0dfaa05 --- /dev/null +++ b/services/java/com/android/server/ServiceWatcher.java @@ -0,0 +1,274 @@ +/* + * 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; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.pm.Signature; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; + +import com.android.internal.content.PackageMonitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +/** + * Find the best Service, and bind to it. + * Handles run-time package changes. + */ +public class ServiceWatcher implements ServiceConnection { + private static final boolean D = false; + private static final String EXTRA_VERSION = "version"; + + private final String mTag; + private final Context mContext; + private final PackageManager mPm; + private final List<HashSet<Signature>> mSignatureSets; + private final String mAction; + private final Runnable mNewServiceWork; + private final Handler mHandler; + + private Object mLock = new Object(); + + // all fields below synchronized on mLock + private IBinder mBinder; // connected service + private String mPackageName; // current best package + private int mVersion; // current best version + + public ServiceWatcher(Context context, String logTag, String action, + List<String> initialPackageNames, Runnable newServiceWork, Handler handler) { + mContext = context; + mTag = logTag; + mAction = action; + mPm = mContext.getPackageManager(); + mNewServiceWork = newServiceWork; + mHandler = handler; + + mSignatureSets = new ArrayList<HashSet<Signature>>(); + for (int i=0; i < initialPackageNames.size(); i++) { + String pkg = initialPackageNames.get(i); + HashSet<Signature> set = new HashSet<Signature>(); + try { + Signature[] sigs = + mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures; + set.addAll(Arrays.asList(sigs)); + mSignatureSets.add(set); + } catch (NameNotFoundException e) { + Log.w(logTag, pkg + " not found"); + } + } + + } + + public boolean start() { + if (!bindBestPackage(null)) return false; + + mPackageMonitor.register(mContext, null, true); + return true; + } + + /** + * Searches and binds to the best package, or do nothing + * if the best package is already bound. + * Only checks the named package, or checks all packages if it + * is null. + * Return true if a new package was found to bind to. + */ + private boolean bindBestPackage(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); + int bestVersion = Integer.MIN_VALUE; + String bestPackage = null; + for (ResolveInfo rInfo : rInfos) { + String packageName = rInfo.serviceInfo.packageName; + + // check signature + try { + PackageInfo pInfo; + pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + if (!isSignatureMatch(pInfo.signatures)) { + Log.w(mTag, packageName + " resolves service " + mAction + + ", but has wrong signature, ignoring"); + continue; + } + } catch (NameNotFoundException e) { + Log.wtf(mTag, e); + continue; + } + + // check version + int version = 0; + if (rInfo.serviceInfo.metaData != null) { + version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0); + } + if (version > mVersion) { + bestVersion = version; + bestPackage = packageName; + } + } + + if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s", + (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "), + rInfos.size(), + (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage))); + + if (bestPackage != null) { + bindToPackage(bestPackage, bestVersion); + return true; + } + return false; + } + + private void unbind() { + String pkg; + synchronized (mLock) { + 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(); + Intent intent = new Intent(mAction); + intent.setPackage(packageName); + synchronized (mLock) { + 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); + } + + private boolean isSignatureMatch(Signature[] signatures) { + if (signatures == null) return false; + + // build hashset of input to test against + HashSet<Signature> inputSet = new HashSet<Signature>(); + for (Signature s : signatures) { + inputSet.add(s); + } + + // test input against each of the signature sets + for (HashSet<Signature> referenceSet : mSignatureSets) { + if (referenceSet.equals(inputSet)) { + return true; + } + } + return false; + } + + private final PackageMonitor mPackageMonitor = new PackageMonitor() { + /** + * Called when package has been reinstalled + */ + @Override + public void onPackageUpdateFinished(String packageName, int uid) { + if (packageName.equals(mPackageName)) { + // package updated, make sure to rebind + unbind(); + } + // 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(); + } + // 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); + } + } + }; + + @Override + public void onServiceConnected(ComponentName name, IBinder binder) { + synchronized (mLock) { + String packageName = name.getPackageName(); + if (packageName.equals(mPackageName)) { + if (D) Log.d(mTag, packageName + " connected"); + mBinder = binder; + if (mHandler !=null && mNewServiceWork != null) { + mHandler.post(mNewServiceWork); + } + } else { + Log.w(mTag, "unexpected onServiceConnected: " + packageName); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + synchronized (mLock) { + String packageName = name.getPackageName(); + if (D) Log.d(mTag, packageName + " disconnected"); + + if (packageName.equals(mPackageName)) { + mBinder = null; + } + } + } + + public String getBestPackageName() { + synchronized (mLock) { + return mPackageName; + } + } + + public int getBestVersion() { + synchronized (mLock) { + return mVersion; + } + } + + public IBinder getBinder() { + synchronized (mLock) { + return mBinder; + } + } +} diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java index d85abe6..a4341b7 100644 --- a/services/java/com/android/server/ShutdownActivity.java +++ b/services/java/com/android/server/ShutdownActivity.java @@ -17,13 +17,12 @@ package com.android.server; import android.app.Activity; -import android.content.BroadcastReceiver; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.util.Slog; -import com.android.server.pm.ShutdownThread; +import com.android.server.power.ShutdownThread; public class ShutdownActivity extends Activity { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e55e7fe..7097891 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -36,14 +36,13 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; -import android.server.BluetoothA2dpService; -import android.server.BluetoothService; import android.server.search.SearchManagerService; import android.service.dreams.DreamManagerService; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.view.Display; import android.view.WindowManager; import com.android.internal.os.BinderInternal; @@ -51,11 +50,15 @@ import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.widget.LockSettingsService; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.am.ActivityManagerService; +import com.android.server.am.BatteryStatsService; +import com.android.server.display.DisplayManagerService; import com.android.server.input.InputManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.ShutdownThread; +import com.android.server.pm.UserManagerService; +import com.android.server.power.PowerManagerService; +import com.android.server.power.ShutdownThread; import com.android.server.usb.UsbService; import com.android.server.wm.WindowManagerService; @@ -118,6 +121,7 @@ class ServerThread extends Thread { ContentService contentService = null; LightsService lights = null; PowerManagerService power = null; + DisplayManagerService display = null; BatteryService battery = null; VibratorService vibrator = null; AlarmManagerService alarm = null; @@ -131,11 +135,11 @@ class ServerThread extends Thread { IPackageManager pm = null; Context context = null; WindowManagerService wm = null; - BluetoothService bluetooth = null; - BluetoothA2dpService bluetoothA2dp = null; + BluetoothManagerService bluetooth = null; DockObserver dock = null; UsbService usb = null; SerialService serial = null; + TwilightService twilight = null; UiModeManagerService uiMode = null; RecognitionManagerService recognition = null; ThrottleService throttle = null; @@ -155,6 +159,10 @@ class ServerThread extends Thread { Slog.i(TAG, "Activity Manager"); context = ActivityManagerService.main(factoryTest); + Slog.i(TAG, "Display Manager"); + display = new DisplayManagerService(context); + ServiceManager.addService(Context.DISPLAY_SERVICE, display, true); + Slog.i(TAG, "Telephony Registry"); ServiceManager.addService("telephony.registry", new TelephonyRegistry(context)); @@ -186,6 +194,11 @@ class ServerThread extends Thread { } ActivityManagerService.setSystemProcess(); + + Slog.i(TAG, "User Service"); + ServiceManager.addService(Context.USER_SERVICE, + UserManagerService.getInstance()); + mContentResolver = context.getContentResolver(); @@ -218,7 +231,8 @@ class ServerThread extends Thread { // only initialize the power service after we have started the // lights service, content providers and the battery service. - power.init(context, lights, ActivityManagerService.self(), battery); + power.init(context, lights, ActivityManagerService.self(), battery, + BatteryStatsService.getService(), display); Slog.i(TAG, "Alarm Manager"); alarm = new AlarmManagerService(context); @@ -229,7 +243,7 @@ class ServerThread extends Thread { ActivityManagerService.self()); Slog.i(TAG, "Window Manager"); - wm = WindowManagerService.main(context, power, + wm = WindowManagerService.main(context, power, display, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot, onlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); @@ -246,23 +260,9 @@ class ServerThread extends Thread { } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) { Slog.i(TAG, "No Bluetooth Service (factory test)"); } else { - Slog.i(TAG, "Bluetooth Service"); - bluetooth = new BluetoothService(context); - ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth); - bluetooth.initAfterRegistration(); - - if (!"0".equals(SystemProperties.get("system_init.startaudioservice"))) { - bluetoothA2dp = new BluetoothA2dpService(context, bluetooth); - ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE, - bluetoothA2dp); - bluetooth.initAfterA2dpRegistration(); - } - - int bluetoothOn = Settings.Secure.getInt(mContentResolver, - Settings.Secure.BLUETOOTH_ON, 0); - if (bluetoothOn != 0) { - bluetooth.enable(); - } + Slog.i(TAG, "Bluetooth Manager Service"); + bluetooth = new BluetoothManagerService(context); + ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth); } } catch (RuntimeException e) { @@ -555,7 +555,7 @@ class ServerThread extends Thread { try { Slog.i(TAG, "Dock Observer"); // Listen for dock station changes - dock = new DockObserver(context, power); + dock = new DockObserver(context); } catch (Throwable e) { reportWtf("starting DockObserver", e); } @@ -587,9 +587,16 @@ class ServerThread extends Thread { } try { + Slog.i(TAG, "Twilight Service"); + twilight = new TwilightService(context); + } catch (Throwable e) { + reportWtf("starting TwilightService", e); + } + + try { Slog.i(TAG, "UI Mode Manager Service"); // Listen for UI mode changes - uiMode = new UiModeManagerService(context); + uiMode = new UiModeManagerService(context, twilight); } catch (Throwable e) { reportWtf("starting UiModeManagerService", e); } @@ -710,6 +717,12 @@ class ServerThread extends Thread { } try { + lockSettings.systemReady(); + } catch (Throwable e) { + reportWtf("making Lock Settings Service ready", e); + } + + try { wm.systemReady(); } catch (Throwable e) { reportWtf("making Window Manager Service ready", e); @@ -728,16 +741,16 @@ class ServerThread extends Thread { w.getDefaultDisplay().getMetrics(metrics); context.getResources().updateConfiguration(config, metrics); - power.systemReady(); try { - pm.systemReady(); + power.systemReady(twilight); } catch (Throwable e) { - reportWtf("making Package Manager Service ready", e); + reportWtf("making Power Manager Service ready", e); } + try { - lockSettings.systemReady(); + pm.systemReady(); } catch (Throwable e) { - reportWtf("making Lock Settings Service ready", e); + reportWtf("making Package Manager Service ready", e); } // These are needed to propagate to the runnable below. @@ -750,6 +763,7 @@ class ServerThread extends Thread { final DockObserver dockF = dock; final UsbService usbF = usb; final ThrottleService throttleF = throttle; + final TwilightService twilightF = twilight; final UiModeManagerService uiModeF = uiMode; final AppWidgetService appWidgetF = appWidget; final WallpaperManagerService wallpaperF = wallpaper; @@ -763,7 +777,6 @@ class ServerThread extends Thread { final StatusBarManagerService statusBarF = statusBar; final DreamManagerService dreamyF = dreamy; final InputManagerService inputManagerF = inputManager; - final BluetoothService bluetoothF = bluetooth; // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state @@ -811,6 +824,11 @@ class ServerThread extends Thread { reportWtf("making USB Service ready", e); } try { + if (twilightF != null) twilightF.systemReady(); + } catch (Throwable e) { + reportWtf("makin Twilight Service ready", e); + } + try { if (uiModeF != null) uiModeF.systemReady(); } catch (Throwable e) { reportWtf("making UI Mode Service ready", e); @@ -876,7 +894,8 @@ class ServerThread extends Thread { reportWtf("making DreamManagerService ready", e); } try { - if (inputManagerF != null) inputManagerF.systemReady(bluetoothF); + // TODO(BT) Pass parameter to input manager + if (inputManagerF != null) inputManagerF.systemReady(); } catch (Throwable e) { reportWtf("making InputManagerService ready", e); } diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index c23a1d9..087e8db 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -35,6 +35,7 @@ import android.text.TextUtils; import android.util.Slog; import java.util.ArrayList; +import java.util.List; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.NetworkInterface; @@ -109,7 +110,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private int mOtaspMode = ServiceStateTracker.OTASP_UNKNOWN; - private CellInfo mCellInfo = null; + private List<CellInfo> mCellInfo = null; static final int PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | @@ -242,7 +243,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } if ((events & PhoneStateListener.LISTEN_CELL_INFO) != 0) { try { - r.callback.onCellInfoChanged(new CellInfo(mCellInfo)); + r.callback.onCellInfoChanged(mCellInfo); } catch (RemoteException ex) { remove(r.binder); } @@ -336,7 +337,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { broadcastSignalStrengthChanged(signalStrength); } - public void notifyCellInfo(CellInfo cellInfo) { + public void notifyCellInfo(List<CellInfo> cellInfo) { if (!checkNotifyPermission("notifyCellInfo()")) { return; } @@ -346,7 +347,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if ((r.events & PhoneStateListener.LISTEN_CELL_INFO) != 0) { try { - r.callback.onCellInfoChanged(new CellInfo(cellInfo)); + r.callback.onCellInfoChanged(cellInfo); } catch (RemoteException ex) { mRemoveList.add(r.binder); } diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index f35a5af..98e6dc0 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -195,6 +195,7 @@ public class ThrottleService extends IThrottleManager.Stub { public void interfaceRemoved(String iface) {} public void limitReached(String limitName, String iface) {} + public void interfaceClassDataActivityChanged(String label, boolean active) {} } diff --git a/services/java/com/android/server/TwilightService.java b/services/java/com/android/server/TwilightService.java new file mode 100644 index 0000000..a7bce54 --- /dev/null +++ b/services/java/com/android/server/TwilightService.java @@ -0,0 +1,572 @@ +/* + * 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; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Slog; + +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; + +import libcore.util.Objects; + +/** + * Figures out whether it's twilight time based on the user's location. + * + * Used by the UI mode manager and other components to adjust night mode + * effects based on sunrise and sunset. + */ +public final class TwilightService { + private static final String TAG = "TwilightService"; + + private static final boolean DEBUG = false; + + private static final String ACTION_UPDATE_TWILIGHT_STATE = + "com.android.server.action.UPDATE_TWILIGHT_STATE"; + + private final Context mContext; + private final AlarmManager mAlarmManager; + private final LocationManager mLocationManager; + private final LocationHandler mLocationHandler; + + private final Object mLock = new Object(); + + private final ArrayList<TwilightListenerRecord> mListeners = + new ArrayList<TwilightListenerRecord>(); + + private boolean mSystemReady; + + private TwilightState mTwilightState; + + public TwilightService(Context context) { + mContext = context; + + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + mLocationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); + mLocationHandler = new LocationHandler(); + } + + void systemReady() { + synchronized (mLock) { + mSystemReady = true; + + IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(ACTION_UPDATE_TWILIGHT_STATE); + mContext.registerReceiver(mUpdateLocationReceiver, filter); + + if (!mListeners.isEmpty()) { + mLocationHandler.enableLocationUpdates(); + } + } + } + + /** + * Gets the current twilight state. + * + * @return The current twilight state, or null if no information is available. + */ + public TwilightState getCurrentState() { + synchronized (mLock) { + return mTwilightState; + } + } + + /** + * Listens for twilight time. + * + * @param listener The listener. + * @param handler The handler on which to post calls into the listener. + */ + public void registerListener(TwilightListener listener, Handler handler) { + synchronized (mLock) { + mListeners.add(new TwilightListenerRecord(listener, handler)); + + if (mSystemReady && mListeners.size() == 1) { + mLocationHandler.enableLocationUpdates(); + } + } + } + + private void setTwilightState(TwilightState state) { + synchronized (mLock) { + if (!Objects.equal(mTwilightState, state)) { + if (DEBUG) { + Slog.d(TAG, "Twilight state changed: " + state); + } + + mTwilightState = state; + int count = mListeners.size(); + for (int i = 0; i < count; i++) { + mListeners.get(i).post(); + } + } + } + } + + // The user has moved if the accuracy circles of the two locations don't overlap. + private static boolean hasMoved(Location from, Location to) { + if (to == null) { + return false; + } + + if (from == null) { + return true; + } + + // if new location is older than the current one, the device hasn't moved. + if (to.getElapsedRealtimeNano() < from.getElapsedRealtimeNano()) { + return false; + } + + // Get the distance between the two points. + float distance = from.distanceTo(to); + + // Get the total accuracy radius for both locations. + float totalAccuracy = from.getAccuracy() + to.getAccuracy(); + + // If the distance is greater than the combined accuracy of the two + // points then they can't overlap and hence the user has moved. + return distance >= totalAccuracy; + } + + /** + * Describes whether it is day or night. + * This object is immutable. + */ + public static final class TwilightState { + private final boolean mIsNight; + private final long mYesterdaySunset; + private final long mTodaySunrise; + private final long mTodaySunset; + private final long mTomorrowSunrise; + + TwilightState(boolean isNight, + long yesterdaySunset, + long todaySunrise, long todaySunset, + long tomorrowSunrise) { + mIsNight = isNight; + mYesterdaySunset = yesterdaySunset; + mTodaySunrise = todaySunrise; + mTodaySunset = todaySunset; + mTomorrowSunrise = tomorrowSunrise; + } + + /** + * Returns true if it is currently night time. + */ + public boolean isNight() { + return mIsNight; + } + + /** + * Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase, + * or -1 if the sun never sets. + */ + public long getYesterdaySunset() { + return mYesterdaySunset; + } + + /** + * Returns the time of today's sunrise in the System.currentTimeMillis() timebase, + * or -1 if the sun never rises. + */ + public long getTodaySunrise() { + return mTodaySunrise; + } + + /** + * Returns the time of today's sunset in the System.currentTimeMillis() timebase, + * or -1 if the sun never sets. + */ + public long getTodaySunset() { + return mTodaySunset; + } + + /** + * Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase, + * or -1 if the sun never rises. + */ + public long getTomorrowSunrise() { + return mTomorrowSunrise; + } + + @Override + public boolean equals(Object o) { + return o instanceof TwilightState && equals((TwilightState)o); + } + + public boolean equals(TwilightState other) { + return other != null + && mIsNight == other.mIsNight + && mYesterdaySunset == other.mYesterdaySunset + && mTodaySunrise == other.mTodaySunrise + && mTodaySunset == other.mTodaySunset + && mTomorrowSunrise == other.mTomorrowSunrise; + } + + @Override + public int hashCode() { + return 0; // don't care + } + + @Override + public String toString() { + DateFormat f = DateFormat.getDateTimeInstance(); + return "{TwilightState: isNight=" + mIsNight + + ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset)) + + ", mTodaySunrise=" + f.format(new Date(mTodaySunrise)) + + ", mTodaySunset=" + f.format(new Date(mTodaySunset)) + + ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise)) + + "}"; + } + } + + /** + * Listener for changes in twilight state. + */ + public interface TwilightListener { + public void onTwilightStateChanged(); + } + + private static final class TwilightListenerRecord implements Runnable { + private final TwilightListener mListener; + private final Handler mHandler; + + public TwilightListenerRecord(TwilightListener listener, Handler handler) { + mListener = listener; + mHandler = handler; + } + + public void post() { + mHandler.post(this); + } + + @Override + public void run() { + mListener.onTwilightStateChanged(); + } + } + + private final class LocationHandler extends Handler { + private static final int MSG_ENABLE_LOCATION_UPDATES = 1; + private static final int MSG_GET_NEW_LOCATION_UPDATE = 2; + private static final int MSG_PROCESS_NEW_LOCATION = 3; + private static final int MSG_DO_TWILIGHT_UPDATE = 4; + + private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS; + private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS; + private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20; + private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000; + private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = + 15 * DateUtils.MINUTE_IN_MILLIS; + private static final double FACTOR_GMT_OFFSET_LONGITUDE = + 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS; + + private boolean mPassiveListenerEnabled; + private boolean mNetworkListenerEnabled; + private boolean mDidFirstInit; + private long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS; + private long mLastUpdateInterval; + private Location mLocation; + private final TwilightCalculator mTwilightCalculator = new TwilightCalculator(); + + public void processNewLocation(Location location) { + Message msg = obtainMessage(MSG_PROCESS_NEW_LOCATION, location); + sendMessage(msg); + } + + public void enableLocationUpdates() { + sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES); + } + + public void requestLocationUpdate() { + sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE); + } + + public void requestTwilightUpdate() { + sendEmptyMessage(MSG_DO_TWILIGHT_UPDATE); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_PROCESS_NEW_LOCATION: { + final Location location = (Location)msg.obj; + final boolean hasMoved = hasMoved(mLocation, location); + final boolean hasBetterAccuracy = mLocation == null + || location.getAccuracy() < mLocation.getAccuracy(); + if (DEBUG) { + Slog.d(TAG, "Processing new location: " + location + + ", hasMoved=" + hasMoved + + ", hasBetterAccuracy=" + hasBetterAccuracy); + } + if (hasMoved || hasBetterAccuracy) { + setLocation(location); + } + break; + } + + case MSG_GET_NEW_LOCATION_UPDATE: + if (!mNetworkListenerEnabled) { + // Don't do anything -- we are still trying to get a + // location. + return; + } + if ((mLastNetworkRegisterTime + MIN_LOCATION_UPDATE_MS) >= + SystemClock.elapsedRealtime()) { + // Don't do anything -- it hasn't been long enough + // since we last requested an update. + return; + } + + // Unregister the current location monitor, so we can + // register a new one for it to get an immediate update. + mNetworkListenerEnabled = false; + mLocationManager.removeUpdates(mEmptyLocationListener); + + // Fall through to re-register listener. + case MSG_ENABLE_LOCATION_UPDATES: + // enable network provider to receive at least location updates for a given + // distance. + boolean networkLocationEnabled; + try { + networkLocationEnabled = + mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); + } catch (Exception e) { + // we may get IllegalArgumentException if network location provider + // does not exist or is not yet installed. + networkLocationEnabled = false; + } + if (!mNetworkListenerEnabled && networkLocationEnabled) { + mNetworkListenerEnabled = true; + mLastNetworkRegisterTime = SystemClock.elapsedRealtime(); + mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, + LOCATION_UPDATE_MS, 0, mEmptyLocationListener); + + if (!mDidFirstInit) { + mDidFirstInit = true; + if (mLocation == null) { + retrieveLocation(); + } + } + } + + // enable passive provider to receive updates from location fixes (gps + // and network). + boolean passiveLocationEnabled; + try { + passiveLocationEnabled = + mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER); + } catch (Exception e) { + // we may get IllegalArgumentException if passive location provider + // does not exist or is not yet installed. + passiveLocationEnabled = false; + } + + if (!mPassiveListenerEnabled && passiveLocationEnabled) { + mPassiveListenerEnabled = true; + mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, + 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener); + } + + if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) { + mLastUpdateInterval *= 1.5; + if (mLastUpdateInterval == 0) { + mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN; + } else if (mLastUpdateInterval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) { + mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX; + } + sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval); + } + break; + + case MSG_DO_TWILIGHT_UPDATE: + updateTwilightState(); + break; + } + } + + private void retrieveLocation() { + Location location = null; + final Iterator<String> providers = + mLocationManager.getProviders(new Criteria(), true).iterator(); + while (providers.hasNext()) { + final Location lastKnownLocation = + mLocationManager.getLastKnownLocation(providers.next()); + // pick the most recent location + if (location == null || (lastKnownLocation != null && + location.getElapsedRealtimeNano() < + lastKnownLocation.getElapsedRealtimeNano())) { + location = lastKnownLocation; + } + } + + // In the case there is no location available (e.g. GPS fix or network location + // is not available yet), the longitude of the location is estimated using the timezone, + // latitude and accuracy are set to get a good average. + if (location == null) { + Time currentTime = new Time(); + currentTime.set(System.currentTimeMillis()); + double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * + (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0)); + location = new Location("fake"); + location.setLongitude(lngOffset); + location.setLatitude(0); + location.setAccuracy(417000.0f); + location.setTime(System.currentTimeMillis()); + location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano()); + + if (DEBUG) { + Slog.d(TAG, "Estimated location from timezone: " + location); + } + } + + setLocation(location); + } + + private void setLocation(Location location) { + mLocation = location; + updateTwilightState(); + } + + private void updateTwilightState() { + if (mLocation == null) { + setTwilightState(null); + return; + } + + final long now = System.currentTimeMillis(); + + // calculate yesterday's twilight + mTwilightCalculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS, + mLocation.getLatitude(), mLocation.getLongitude()); + final long yesterdaySunset = mTwilightCalculator.mSunset; + + // calculate today's twilight + mTwilightCalculator.calculateTwilight(now, + mLocation.getLatitude(), mLocation.getLongitude()); + final boolean isNight = (mTwilightCalculator.mState == TwilightCalculator.NIGHT); + final long todaySunrise = mTwilightCalculator.mSunrise; + final long todaySunset = mTwilightCalculator.mSunset; + + // calculate tomorrow's twilight + mTwilightCalculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS, + mLocation.getLatitude(), mLocation.getLongitude()); + final long tomorrowSunrise = mTwilightCalculator.mSunrise; + + // set twilight state + TwilightState state = new TwilightState(isNight, yesterdaySunset, + todaySunrise, todaySunset, tomorrowSunrise); + if (DEBUG) { + Slog.d(TAG, "Updating twilight state: " + state); + } + setTwilightState(state); + + // schedule next update + long nextUpdate = 0; + if (todaySunrise == -1 || todaySunset == -1) { + // In the case the day or night never ends the update is scheduled 12 hours later. + nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS; + } else { + // add some extra time to be on the safe side. + nextUpdate += DateUtils.MINUTE_IN_MILLIS; + + if (now > todaySunset) { + nextUpdate += tomorrowSunrise; + } else if (now > todaySunrise) { + nextUpdate += todaySunset; + } else { + nextUpdate += todaySunrise; + } + } + + if (DEBUG) { + Slog.d(TAG, "Next update in " + (nextUpdate - now) + " ms"); + } + + Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0); + mAlarmManager.cancel(pendingIntent); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent); + } + }; + + private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction()) + && !intent.getBooleanExtra("state", false)) { + // Airplane mode is now off! + mLocationHandler.requestLocationUpdate(); + return; + } + + // Time zone has changed or alarm expired. + mLocationHandler.requestTwilightUpdate(); + } + }; + + // A LocationListener to initialize the network location provider. The location updates + // are handled through the passive location provider. + private final LocationListener mEmptyLocationListener = new LocationListener() { + public void onLocationChanged(Location location) { + } + + public void onProviderDisabled(String provider) { + } + + public void onProviderEnabled(String provider) { + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + } + }; + + private final LocationListener mLocationListener = new LocationListener() { + public void onLocationChanged(Location location) { + mLocationHandler.processNewLocation(location); + } + + public void onProviderDisabled(String provider) { + } + + public void onProviderEnabled(String provider) { + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + } + }; +} diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index d1f92a7..617b29d 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -18,7 +18,6 @@ package com.android.server; import android.app.Activity; import android.app.ActivityManagerNative; -import android.app.AlarmManager; import android.app.IUiModeManager; import android.app.Notification; import android.app.NotificationManager; @@ -32,55 +31,33 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; import android.os.BatteryManager; import android.os.Binder; -import android.os.Bundle; import android.os.Handler; -import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.provider.Settings; -import android.text.format.DateUtils; -import android.text.format.Time; import android.util.Slog; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Iterator; import com.android.internal.R; import com.android.internal.app.DisableCarModeActivity; +import com.android.server.TwilightService.TwilightState; class UiModeManagerService extends IUiModeManager.Stub { private static final String TAG = UiModeManager.class.getSimpleName(); private static final boolean LOG = false; - private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL"; - // Enable launching of applications when entering the dock. private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true; private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; - private static final int MSG_UPDATE_TWILIGHT = 0; - private static final int MSG_ENABLE_LOCATION_UPDATES = 1; - private static final int MSG_GET_NEW_LOCATION_UPDATE = 2; - - private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS; - private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS; - private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20; - private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000; - private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 15 * DateUtils.MINUTE_IN_MILLIS; - private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS; - - private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE"; - private final Context mContext; + private final TwilightService mTwilightService; + private final Handler mHandler = new Handler(); final Object mLock = new Object(); @@ -106,10 +83,6 @@ class UiModeManagerService extends IUiModeManager.Stub { private NotificationManager mNotificationManager; - private AlarmManager mAlarmManager; - - private LocationManager mLocationManager; - private Location mLocation; private StatusBarManager mStatusBarManager; private final PowerManager.WakeLock mWakeLock; @@ -206,15 +179,6 @@ class UiModeManagerService extends IUiModeManager.Stub { } }; - private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { - mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT); - } - } - }; - private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -236,113 +200,24 @@ class UiModeManagerService extends IUiModeManager.Stub { } }; - private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() { + private final TwilightService.TwilightListener mTwilightListener = + new TwilightService.TwilightListener() { @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) { - if (!intent.getBooleanExtra("state", false)) { - // Airplane mode is now off! - mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE); - } - } else { - // Time zone has changed! - mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE); - } - } - }; - - // A LocationListener to initialize the network location provider. The location updates - // are handled through the passive location provider. - private final LocationListener mEmptyLocationListener = new LocationListener() { - public void onLocationChanged(Location location) { - } - - public void onProviderDisabled(String provider) { - } - - public void onProviderEnabled(String provider) { - } - - public void onStatusChanged(String provider, int status, Bundle extras) { - } - }; - - private final LocationListener mLocationListener = new LocationListener() { - - public void onLocationChanged(Location location) { - final boolean hasMoved = hasMoved(location); - final boolean hasBetterAccuracy = mLocation == null - || location.getAccuracy() < mLocation.getAccuracy(); - if (hasMoved || hasBetterAccuracy) { - synchronized (mLock) { - mLocation = location; - if (hasMoved && isDoingNightMode() - && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { - mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT); - } - } - } - } - - public void onProviderDisabled(String provider) { - } - - public void onProviderEnabled(String provider) { - } - - public void onStatusChanged(String provider, int status, Bundle extras) { - } - - /* - * The user has moved if the accuracy circles of the two locations - * don't overlap. - */ - private boolean hasMoved(Location location) { - if (location == null) { - return false; - } - if (mLocation == null) { - return true; - } - - /* if new location is older than the current one, the devices hasn't - * moved. - */ - if (location.getTime() < mLocation.getTime()) { - return false; - } - - /* Get the distance between the two points */ - float distance = mLocation.distanceTo(location); - - /* Get the total accuracy radius for both locations */ - float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy(); - - /* If the distance is greater than the combined accuracy of the two - * points then they can't overlap and hence the user has moved. - */ - return distance >= totalAccuracy; + public void onTwilightStateChanged() { + updateTwilight(); } }; - public UiModeManagerService(Context context) { + public UiModeManagerService(Context context, TwilightService twilight) { mContext = context; + mTwilightService = twilight; ServiceManager.addService(Context.UI_MODE_SERVICE, this); - mAlarmManager = - (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); - mLocationManager = - (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); - mContext.registerReceiver(mTwilightUpdateReceiver, - new IntentFilter(ACTION_UPDATE_NIGHT_MODE)); mContext.registerReceiver(mDockModeReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT)); mContext.registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - mContext.registerReceiver(mUpdateLocationReceiver, filter); PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); @@ -360,6 +235,8 @@ class UiModeManagerService extends IUiModeManager.Stub { mNightMode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO); + + mTwilightService.registerListener(mTwilightListener, mHandler); } public void disableCarMode(int flags) { @@ -419,8 +296,8 @@ class UiModeManagerService extends IUiModeManager.Stub { synchronized (mLock) { mSystemReady = true; mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR; + updateComputedNightModeLocked(); updateLocked(0, 0); - mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES); } } @@ -467,7 +344,7 @@ class UiModeManagerService extends IUiModeManager.Stub { } if (mCarModeEnabled) { if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) { - updateTwilightLocked(); + updateComputedNightModeLocked(); uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO; } else { @@ -651,189 +528,20 @@ class UiModeManagerService extends IUiModeManager.Stub { } } - private final Handler mHandler = new Handler() { - - boolean mPassiveListenerEnabled; - boolean mNetworkListenerEnabled; - boolean mDidFirstInit; - long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS; - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE_TWILIGHT: - synchronized (mLock) { - if (isDoingNightMode() && mLocation != null - && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { - updateTwilightLocked(); - updateLocked(0, 0); - } - } - break; - case MSG_GET_NEW_LOCATION_UPDATE: - if (!mNetworkListenerEnabled) { - // Don't do anything -- we are still trying to get a - // location. - return; - } - if ((mLastNetworkRegisterTime+MIN_LOCATION_UPDATE_MS) - >= SystemClock.elapsedRealtime()) { - // Don't do anything -- it hasn't been long enough - // since we last requested an update. - return; - } - - // Unregister the current location monitor, so we can - // register a new one for it to get an immediate update. - mNetworkListenerEnabled = false; - mLocationManager.removeUpdates(mEmptyLocationListener); - - // Fall through to re-register listener. - case MSG_ENABLE_LOCATION_UPDATES: - // enable network provider to receive at least location updates for a given - // distance. - boolean networkLocationEnabled; - try { - networkLocationEnabled = - mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); - } catch (Exception e) { - // we may get IllegalArgumentException if network location provider - // does not exist or is not yet installed. - networkLocationEnabled = false; - } - if (!mNetworkListenerEnabled && networkLocationEnabled) { - mNetworkListenerEnabled = true; - mLastNetworkRegisterTime = SystemClock.elapsedRealtime(); - mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, - LOCATION_UPDATE_MS, 0, mEmptyLocationListener); - - if (!mDidFirstInit) { - mDidFirstInit = true; - if (mLocation == null) { - retrieveLocation(); - } - synchronized (mLock) { - if (isDoingNightMode() && mLocation != null - && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { - updateTwilightLocked(); - updateLocked(0, 0); - } - } - } - } - // enable passive provider to receive updates from location fixes (gps - // and network). - boolean passiveLocationEnabled; - try { - passiveLocationEnabled = - mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER); - } catch (Exception e) { - // we may get IllegalArgumentException if passive location provider - // does not exist or is not yet installed. - passiveLocationEnabled = false; - } - if (!mPassiveListenerEnabled && passiveLocationEnabled) { - mPassiveListenerEnabled = true; - mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, - 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener); - } - if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) { - long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL); - interval *= 1.5; - if (interval == 0) { - interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN; - } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) { - interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX; - } - Bundle bundle = new Bundle(); - bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval); - Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES); - newMsg.setData(bundle); - mHandler.sendMessageDelayed(newMsg, interval); - } - break; - } - } - - private void retrieveLocation() { - Location location = null; - final Iterator<String> providers = - mLocationManager.getProviders(new Criteria(), true).iterator(); - while (providers.hasNext()) { - final Location lastKnownLocation = - mLocationManager.getLastKnownLocation(providers.next()); - // pick the most recent location - if (location == null || (lastKnownLocation != null && - location.getTime() < lastKnownLocation.getTime())) { - location = lastKnownLocation; - } - } - // In the case there is no location available (e.g. GPS fix or network location - // is not available yet), the longitude of the location is estimated using the timezone, - // latitude and accuracy are set to get a good average. - if (location == null) { - Time currentTime = new Time(); - currentTime.set(System.currentTimeMillis()); - double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * - (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0)); - location = new Location("fake"); - location.setLongitude(lngOffset); - location.setLatitude(0); - location.setAccuracy(417000.0f); - location.setTime(System.currentTimeMillis()); - } - synchronized (mLock) { - mLocation = location; + private void updateTwilight() { + synchronized (mLock) { + if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + updateComputedNightModeLocked(); + updateLocked(0, 0); } } - }; - - void updateTwilightLocked() { - if (mLocation == null) { - return; - } - final long currentTime = System.currentTimeMillis(); - boolean nightMode; - // calculate current twilight - TwilightCalculator tw = new TwilightCalculator(); - tw.calculateTwilight(currentTime, - mLocation.getLatitude(), mLocation.getLongitude()); - if (tw.mState == TwilightCalculator.DAY) { - nightMode = false; - } else { - nightMode = true; - } - - // schedule next update - long nextUpdate = 0; - if (tw.mSunrise == -1 || tw.mSunset == -1) { - // In the case the day or night never ends the update is scheduled 12 hours later. - nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS; - } else { - final int mLastTwilightState = tw.mState; - // add some extra time to be on the save side. - nextUpdate += DateUtils.MINUTE_IN_MILLIS; - if (currentTime > tw.mSunset) { - // next update should be on the following day - tw.calculateTwilight(currentTime - + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(), - mLocation.getLongitude()); - } + } - if (mLastTwilightState == TwilightCalculator.NIGHT) { - nextUpdate += tw.mSunrise; - } else { - nextUpdate += tw.mSunset; - } + private void updateComputedNightModeLocked() { + TwilightState state = mTwilightService.getCurrentState(); + if (state != null) { + mComputedNightMode = state.isNight(); } - - Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE); - PendingIntent pendingIntent = - PendingIntent.getBroadcast(mContext, 0, updateIntent, 0); - mAlarmManager.cancel(pendingIntent); - mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent); - - mComputedNightMode = nightMode; } @Override @@ -858,9 +566,8 @@ class UiModeManagerService extends IUiModeManager.Stub { pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); pw.print(" mSystemReady="); pw.println(mSystemReady); - if (mLocation != null) { - pw.print(" mLocation="); pw.println(mLocation); - } + pw.print(" mTwilightService.getCurrentState()="); + pw.println(mTwilightService.getCurrentState()); } } } diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 8a08277..afd7d0e 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -48,7 +48,7 @@ import android.os.RemoteCallbackList; import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.UserId; +import android.os.UserHandle; import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.IWallpaperService; @@ -423,9 +423,9 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_USER_SWITCHED.equals(action)) { - switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0)); + switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { - removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0)); + removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } } }, userFilter); @@ -484,7 +484,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void clearWallpaper() { if (DEBUG) Slog.v(TAG, "clearWallpaper"); synchronized (mLock) { - clearWallpaperLocked(false, UserId.getCallingUserId()); + clearWallpaperLocked(false, UserHandle.getCallingUserId()); } } @@ -521,7 +521,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void setDimensionHints(int width, int height) throws RemoteException { checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); WallpaperData wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); @@ -552,14 +552,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public int getWidthHint() throws RemoteException { synchronized (mLock) { - WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId()); + WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); return wallpaper.width; } } public int getHeightHint() throws RemoteException { synchronized (mLock) { - WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId()); + WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); return wallpaper.height; } } @@ -574,7 +574,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { if (callingUid == android.os.Process.SYSTEM_UID) { wallpaperUserId = mCurrentUserId; } else { - wallpaperUserId = UserId.getUserId(callingUid); + wallpaperUserId = UserHandle.getUserId(callingUid); } WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId); try { @@ -597,7 +597,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } public WallpaperInfo getWallpaperInfo() { - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); synchronized (mLock) { WallpaperData wallpaper = mWallpaperMap.get(userId); if (wallpaper.connection != null) { @@ -609,7 +609,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public ParcelFileDescriptor setWallpaper(String name) { if (DEBUG) Slog.v(TAG, "setWallpaper"); - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); WallpaperData wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); @@ -656,7 +656,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void setWallpaperComponent(ComponentName name) { if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); WallpaperData wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); @@ -768,10 +768,6 @@ class WallpaperManagerService extends IWallpaperManager.Stub { WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper); intent.setComponent(componentName); int serviceUserId = wallpaper.userId; - // Because the image wallpaper is running in the system ui - if (componentName.equals(wallpaper.imageWallpaperComponent)) { - serviceUserId = 0; - } intent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.wallpaper_binding_label); intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index c239382..9edfad6 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.server.am.ActivityManagerService; +import com.android.server.power.PowerManagerService; import android.app.AlarmManager; import android.app.PendingIntent; @@ -348,7 +349,7 @@ public class Watchdog extends Thread { } if (mMinScreenOff >= 0 && (mPower == null || - mPower.timeSinceScreenOn() < mMinScreenOff)) { + mPower.timeSinceScreenWasLastOn() < mMinScreenOff)) { return "screen"; } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 1f03d17..f483576 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -58,6 +58,7 @@ import android.os.SystemProperties; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; +import android.util.Log; import android.util.Slog; import java.util.ArrayList; @@ -110,10 +111,6 @@ public class WifiService extends IWifiManager.Stub { private int mScanLocksAcquired; private int mScanLocksReleased; - /* A mapping from UID to scan count */ - private HashMap<Integer, Integer> mScanCount = - new HashMap<Integer, Integer>(); - private final List<Multicaster> mMulticasters = new ArrayList<Multicaster>(); private int mMulticastEnabled; @@ -161,6 +158,10 @@ public class WifiService extends IWifiManager.Stub { /* Tracks whether wifi is enabled from WifiStateMachine's perspective */ private boolean mWifiEnabled; + /* The work source (UID) that triggered the current WIFI scan, synchronized + * on this */ + private WorkSource mScanWorkSource; + private boolean mIsReceiverRegistered = false; @@ -302,6 +303,10 @@ public class WifiService extends IWifiManager.Stub { mWifiStateMachine.sendMessage(Message.obtain(msg)); break; } + case WifiManager.RSSI_PKTCNT_FETCH: { + mWifiStateMachine.sendMessage(Message.obtain(msg)); + break; + } default: { Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg); break; @@ -413,6 +418,7 @@ public class WifiService extends IWifiManager.Stub { } } else if (intent.getAction().equals( WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { + noteScanEnd(); checkAndSetNotification(); } } @@ -430,6 +436,44 @@ public class WifiService extends IWifiManager.Stub { mNotificationEnabledSettingObserver.register(); } + /** Tell battery stats about a new WIFI scan */ + private void noteScanStart() { + WorkSource scanWorkSource = null; + synchronized (WifiService.this) { + if (mScanWorkSource != null) { + // Scan already in progress, don't add this one to battery stats + return; + } + scanWorkSource = new WorkSource(Binder.getCallingUid()); + mScanWorkSource = scanWorkSource; + } + + long id = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteWifiScanStartedFromSource(scanWorkSource); + } catch (RemoteException e) { + Log.w(TAG, e); + } finally { + Binder.restoreCallingIdentity(id); + } + } + + /** Tell battery stats that the current WIFI scan has completed */ + private void noteScanEnd() { + WorkSource scanWorkSource = null; + synchronized (WifiService.this) { + scanWorkSource = mScanWorkSource; + mScanWorkSource = null; + } + if (scanWorkSource != null) { + try { + mBatteryStats.noteWifiScanStoppedFromSource(scanWorkSource); + } catch (RemoteException e) { + Log.w(TAG, e); + } + } + } + /** * Check if Wi-Fi needs to be enabled and start * if needed @@ -541,16 +585,8 @@ public class WifiService extends IWifiManager.Stub { */ public void startScan(boolean forceActive) { enforceChangePermission(); - - int uid = Binder.getCallingUid(); - int count = 0; - synchronized (mScanCount) { - if (mScanCount.containsKey(uid)) { - count = mScanCount.get(uid); - } - mScanCount.put(uid, ++count); - } mWifiStateMachine.startScan(forceActive); + noteScanStart(); } private void enforceAccessPermission() { @@ -923,10 +959,6 @@ public class WifiService extends IWifiManager.Stub { * an AsyncChannel communication with WifiService */ public Messenger getWifiServiceMessenger() { - /* Enforce the highest permissions - TODO: when we consider exposing the asynchronous API, think about - how to provide both access and change permissions seperately - */ enforceAccessPermission(); enforceChangePermission(); return new Messenger(mAsyncServiceHandler); @@ -1011,12 +1043,6 @@ public class WifiService extends IWifiManager.Stub { mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); } - //Start scan stats tracking when device unplugged - if (pluggedType == 0) { - synchronized (mScanCount) { - mScanCount.clear(); - } - } mPluggedType = pluggedType; } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, @@ -1207,13 +1233,6 @@ public class WifiService extends IWifiManager.Stub { pw.println("Locks held:"); mLocks.dump(pw); - pw.println("Scan count since last plugged in"); - synchronized (mScanCount) { - for(int sc : mScanCount.keySet()) { - pw.println("UID: " + sc + " Scan count: " + mScanCount.get(sc)); - } - } - pw.println(); pw.println("WifiWatchdogStateMachine dump"); mWifiWatchdogStateMachine.dump(pw); @@ -1333,10 +1352,8 @@ public class WifiService extends IWifiManager.Stub { switch(wifiLock.mMode) { case WifiManager.WIFI_MODE_FULL: case WifiManager.WIFI_MODE_FULL_HIGH_PERF: - mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource); - break; case WifiManager.WIFI_MODE_SCAN_ONLY: - mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource); + mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource); break; } } @@ -1345,10 +1362,8 @@ public class WifiService extends IWifiManager.Stub { switch(wifiLock.mMode) { case WifiManager.WIFI_MODE_FULL: case WifiManager.WIFI_MODE_FULL_HIGH_PERF: - mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); - break; case WifiManager.WIFI_MODE_SCAN_ONLY: - mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource); + mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); break; } } diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java index 96ac493..56c0fdf 100644 --- a/services/java/com/android/server/WiredAccessoryObserver.java +++ b/services/java/com/android/server/WiredAccessoryObserver.java @@ -16,12 +16,12 @@ package com.android.server; -import android.app.ActivityManagerNative; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; @@ -39,7 +39,7 @@ import java.util.List; /** * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock. */ -class WiredAccessoryObserver extends UEventObserver { +final class WiredAccessoryObserver extends UEventObserver { private static final String TAG = WiredAccessoryObserver.class.getSimpleName(); private static final boolean LOG = true; private static final int BIT_HEADSET = (1 << 0); @@ -50,122 +50,32 @@ class WiredAccessoryObserver extends UEventObserver { private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| BIT_HDMI_AUDIO); - private static final int HEADSETS_WITH_MIC = BIT_HEADSET; - private static class UEventInfo { - private final String mDevName; - private final int mState1Bits; - private final int mState2Bits; - - public UEventInfo(String devName, int state1Bits, int state2Bits) { - mDevName = devName; - mState1Bits = state1Bits; - mState2Bits = state2Bits; - } - - public String getDevName() { return mDevName; } - - public String getDevPath() { - return String.format("/devices/virtual/switch/%s", mDevName); - } - - public String getSwitchStatePath() { - return String.format("/sys/class/switch/%s/state", mDevName); - } - - public boolean checkSwitchExists() { - File f = new File(getSwitchStatePath()); - return ((null != f) && f.exists()); - } - - public int computeNewHeadsetState(int headsetState, int switchState) { - int preserveMask = ~(mState1Bits | mState2Bits); - int setBits = ((switchState == 1) ? mState1Bits : - ((switchState == 2) ? mState2Bits : 0)); - - return ((headsetState & preserveMask) | setBits); - } - } - - private static List<UEventInfo> makeObservedUEventList() { - List<UEventInfo> retVal = new ArrayList<UEventInfo>(); - UEventInfo uei; - - // Monitor h2w - uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - Slog.w(TAG, "This kernel does not have wired headset support"); - } - - // Monitor USB - uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - Slog.w(TAG, "This kernel does not have usb audio support"); - } + private final Object mLock = new Object(); - // Monitor HDMI - // - // If the kernel has support for the "hdmi_audio" switch, use that. It will be signalled - // only when the HDMI driver has a video mode configured, and the downstream sink indicates - // support for audio in its EDID. - // - // If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi" - // switch instead. - uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - Slog.w(TAG, "This kernel does not have HDMI audio support"); - } - } - - return retVal; - } - - private static List<UEventInfo> uEventInfo = makeObservedUEventList(); + private final Context mContext; + private final WakeLock mWakeLock; // held while there is a pending route change + private final AudioManager mAudioManager; + private final List<UEventInfo> mUEventInfo; private int mHeadsetState; private int mPrevHeadsetState; private String mHeadsetName; - private final Context mContext; - private final WakeLock mWakeLock; // held while there is a pending route change - - private final AudioManager mAudioManager; - public WiredAccessoryObserver(Context context) { mContext = context; + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver"); mWakeLock.setReferenceCounted(false); mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + mUEventInfo = makeObservedUEventList(); + context.registerReceiver(new BootCompletedReceiver(), new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); } - private final class BootCompletedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - // At any given time accessories could be inserted - // one on the board, one on the dock and one on HDMI: - // observe three UEVENTs - init(); // set initial status - for (int i = 0; i < uEventInfo.size(); ++i) { - UEventInfo uei = uEventInfo.get(i); - startObserving("DEVPATH="+uei.getDevPath()); - } - } - } - @Override public void onUEvent(UEventObserver.UEvent event) { if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); @@ -174,56 +84,64 @@ class WiredAccessoryObserver extends UEventObserver { String devPath = event.get("DEVPATH"); String name = event.get("SWITCH_NAME"); int state = Integer.parseInt(event.get("SWITCH_STATE")); - updateState(devPath, name, state); + synchronized (mLock) { + updateStateLocked(devPath, name, state); + } } catch (NumberFormatException e) { Slog.e(TAG, "Could not parse switch state from event " + event); } } - private synchronized final void updateState(String devPath, String name, int state) - { - for (int i = 0; i < uEventInfo.size(); ++i) { - UEventInfo uei = uEventInfo.get(i); - if (devPath.equals(uei.getDevPath())) { - update(name, uei.computeNewHeadsetState(mHeadsetState, state)); - return; + private void bootCompleted() { + synchronized (mLock) { + char[] buffer = new char[1024]; + mPrevHeadsetState = mHeadsetState; + + if (LOG) Slog.v(TAG, "init()"); + + for (int i = 0; i < mUEventInfo.size(); ++i) { + UEventInfo uei = mUEventInfo.get(i); + try { + int curState; + FileReader file = new FileReader(uei.getSwitchStatePath()); + int len = file.read(buffer, 0, 1024); + file.close(); + curState = Integer.valueOf((new String(buffer, 0, len)).trim()); + + if (curState > 0) { + updateStateLocked(uei.getDevPath(), uei.getDevName(), curState); + } + } catch (FileNotFoundException e) { + Slog.w(TAG, uei.getSwitchStatePath() + + " not found while attempting to determine initial switch state"); + } catch (Exception e) { + Slog.e(TAG, "" , e); + } } } - } - - private synchronized final void init() { - char[] buffer = new char[1024]; - mPrevHeadsetState = mHeadsetState; - - if (LOG) Slog.v(TAG, "init()"); - for (int i = 0; i < uEventInfo.size(); ++i) { - UEventInfo uei = uEventInfo.get(i); - try { - int curState; - FileReader file = new FileReader(uei.getSwitchStatePath()); - int len = file.read(buffer, 0, 1024); - file.close(); - curState = Integer.valueOf((new String(buffer, 0, len)).trim()); - - if (curState > 0) { - updateState(uei.getDevPath(), uei.getDevName(), curState); - } + // At any given time accessories could be inserted + // one on the board, one on the dock and one on HDMI: + // observe three UEVENTs + for (int i = 0; i < mUEventInfo.size(); ++i) { + UEventInfo uei = mUEventInfo.get(i); + startObserving("DEVPATH="+uei.getDevPath()); + } + } - } catch (FileNotFoundException e) { - Slog.w(TAG, uei.getSwitchStatePath() + - " not found while attempting to determine initial switch state"); - } catch (Exception e) { - Slog.e(TAG, "" , e); + private void updateStateLocked(String devPath, String name, int state) { + for (int i = 0; i < mUEventInfo.size(); ++i) { + UEventInfo uei = mUEventInfo.get(i); + if (devPath.equals(uei.getDevPath())) { + updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state)); + return; } } } - private synchronized final void update(String newName, int newState) { + private void updateLocked(String newName, int newState) { // Retain only relevant bits int headsetState = newState & SUPPORTED_HEADSETS; - int newOrOld = headsetState | mHeadsetState; - int delay = 0; int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG; int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL; int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC); @@ -254,28 +172,26 @@ class WiredAccessoryObserver extends UEventObserver { mHeadsetState = headsetState; mWakeLock.acquire(); - mHandler.sendMessage(mHandler.obtainMessage(0, - mHeadsetState, - mPrevHeadsetState, - mHeadsetName)); + + Message msg = mHandler.obtainMessage(0, mHeadsetState, mPrevHeadsetState, mHeadsetName); + mHandler.sendMessage(msg); } - private synchronized final void setDevicesState(int headsetState, - int prevHeadsetState, - String headsetName) { - int allHeadsets = SUPPORTED_HEADSETS; - for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { - if ((curHeadset & allHeadsets) != 0) { - setDeviceState(curHeadset, headsetState, prevHeadsetState, headsetName); - allHeadsets &= ~curHeadset; + private void setDevicesState( + int headsetState, int prevHeadsetState, String headsetName) { + synchronized (mLock) { + int allHeadsets = SUPPORTED_HEADSETS; + for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { + if ((curHeadset & allHeadsets) != 0) { + setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName); + allHeadsets &= ~curHeadset; + } } } } - private final void setDeviceState(int headset, - int headsetState, - int prevHeadsetState, - String headsetName) { + private void setDeviceStateLocked(int headset, + int headsetState, int prevHeadsetState, String headsetName) { if ((headsetState & headset) != (prevHeadsetState & headset)) { int device; int state; @@ -308,11 +224,96 @@ class WiredAccessoryObserver extends UEventObserver { } } - private final Handler mHandler = new Handler() { + private static List<UEventInfo> makeObservedUEventList() { + List<UEventInfo> retVal = new ArrayList<UEventInfo>(); + UEventInfo uei; + + // Monitor h2w + uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + Slog.w(TAG, "This kernel does not have wired headset support"); + } + + // Monitor USB + uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + Slog.w(TAG, "This kernel does not have usb audio support"); + } + + // Monitor HDMI + // + // If the kernel has support for the "hdmi_audio" switch, use that. It will be signalled + // only when the HDMI driver has a video mode configured, and the downstream sink indicates + // support for audio in its EDID. + // + // If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi" + // switch instead. + uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + Slog.w(TAG, "This kernel does not have HDMI audio support"); + } + } + + return retVal; + } + + private final Handler mHandler = new Handler(Looper.myLooper(), null, true) { @Override public void handleMessage(Message msg) { setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); mWakeLock.release(); } }; + + private static final class UEventInfo { + private final String mDevName; + private final int mState1Bits; + private final int mState2Bits; + + public UEventInfo(String devName, int state1Bits, int state2Bits) { + mDevName = devName; + mState1Bits = state1Bits; + mState2Bits = state2Bits; + } + + public String getDevName() { return mDevName; } + + public String getDevPath() { + return String.format("/devices/virtual/switch/%s", mDevName); + } + + public String getSwitchStatePath() { + return String.format("/sys/class/switch/%s/state", mDevName); + } + + public boolean checkSwitchExists() { + File f = new File(getSwitchStatePath()); + return ((null != f) && f.exists()); + } + + public int computeNewHeadsetState(int headsetState, int switchState) { + int preserveMask = ~(mState1Bits | mState2Bits); + int setBits = ((switchState == 1) ? mState1Bits : + ((switchState == 2) ? mState2Bits : 0)); + + return ((headsetState & preserveMask) | setBits); + } + } + + private final class BootCompletedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + bootCompleted(); + } + } } diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java index 8fa6722..fc774d4 100644 --- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -16,13 +16,12 @@ package com.android.server.accessibility; -import com.android.server.input.InputFilter; - import android.content.Context; import android.os.PowerManager; import android.util.Slog; import android.view.InputDevice; import android.view.InputEvent; +import android.view.InputFilter; import android.view.MotionEvent; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index e42ec84..857334e 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -58,6 +58,7 @@ import android.text.TextUtils.SimpleStringSplitter; import android.util.Slog; import android.util.SparseArray; import android.view.IWindow; +import android.view.IWindowManager; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -74,7 +75,6 @@ import android.view.accessibility.IAccessibilityManagerClient; import com.android.internal.R; import com.android.internal.content.PackageMonitor; import com.android.internal.statusbar.IStatusBarService; -import com.android.server.wm.WindowManagerService; import org.xmlpull.v1.XmlPullParserException; @@ -157,7 +157,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private boolean mIsTouchExplorationEnabled; - private final WindowManagerService mWindowManagerService; + private final IWindowManager mWindowManager; private final SecurityPolicy mSecurityPolicy; @@ -181,8 +181,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public AccessibilityManagerService(Context context) { mContext = context; mPackageManager = mContext.getPackageManager(); - mWindowManagerService = (WindowManagerService) ServiceManager.getService( - Context.WINDOW_SERVICE); + mWindowManager = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE); mSecurityPolicy = new SecurityPolicy(); mMainHandler = new MainHanler(); registerPackageChangeAndBootCompletedBroadcastReceiver(); @@ -358,8 +357,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // We will update when the automation service dies. if (mUiAutomationService == null) { populateTouchExplorationGrantedAccessibilityServicesLocked(); - unbindAllServicesLocked(); - manageServicesLocked(); + handleTouchExplorationGrantedAccessibilityServicesChangedLocked(); } } } @@ -584,15 +582,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { * * @param outBounds The output to which to write the bounds. */ - void getActiveWindowBounds(Rect outBounds) { + boolean getActiveWindowBounds(Rect outBounds) { synchronized (mLock) { final int windowId = mSecurityPolicy.mActiveWindowId; IBinder token = mWindowIdToWindowTokenMap.get(windowId); - mWindowManagerService.getWindowFrame(token, outBounds); + try { + mWindowManager.getWindowFrame(token, outBounds); + return true; + } catch (RemoteException re) { + /* ignore */ + } + return false; } } - int getActiveWindowId() { + public int getActiveWindowId() { return mSecurityPolicy.mActiveWindowId; } @@ -624,7 +628,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // enabled accessibility services. for (int i = mServices.size() - 1; i >= 0; i--) { Service service = mServices.get(i); - if (service.mReqeustTouchExplorationMode && service.mIsDefault == isDefault) { + if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) { service.notifyGesture(gestureId); return true; } @@ -967,13 +971,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (mInputFilter == null) { mInputFilter = new AccessibilityInputFilter(mContext, this); } - mWindowManagerService.setInputFilter(mInputFilter); + try { + mWindowManager.setInputFilter(mInputFilter); + } catch (RemoteException re) { + /* ignore */ + } } return; } if (mHasInputFilter) { mHasInputFilter = false; - mWindowManagerService.setInputFilter(null); + try { + mWindowManager.setInputFilter(null); + } catch (RemoteException re) { + /* ignore */ + } } } @@ -1000,6 +1012,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1; } + private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked() { + final int serviceCount = mServices.size(); + for (int i = 0; i < serviceCount; i++) { + Service service = mServices.get(i); + if (service.mRequestTouchExplorationMode + && mTouchExplorationGrantedServices.contains(service.mComponentName)) { + tryEnableTouchExplorationLocked(service); + return; + } + } + if (mIsTouchExplorationEnabled) { + mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 0, + 0).sendToTarget(); + } + } + private void tryEnableTouchExplorationLocked(final Service service) { if (!mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) { final boolean canToggleTouchExploration = mTouchExplorationGrantedServices.contains( @@ -1163,8 +1191,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { boolean mCanRetrieveScreenContent; - boolean mReqeustTouchExplorationMode; - boolean mIsAutomation; final Rect mTempBounds = new Rect(); @@ -1204,7 +1230,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mIsAutomation = isAutomation; if (!isAutomation) { mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent(); - mReqeustTouchExplorationMode = + mRequestTouchExplorationMode = (accessibilityServiceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; mIntent = new Intent().setComponent(mComponentName); @@ -1334,8 +1360,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); - final int windowLeft; - final int windowTop; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { mSecurityPolicy.enforceCanRetrieveWindowContent(this); @@ -1348,10 +1372,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return 0; } } - IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId); - mWindowManagerService.getWindowFrame(token, mTempBounds); - windowLeft = mTempBounds.left; - windowTop = mTempBounds.top; } final int flags = (mIncludeNotImportantViews) ? AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; @@ -1359,8 +1379,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final long identityToken = Binder.clearCallingIdentity(); try { connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId, - windowLeft, windowTop, interactionId, callback, flags, interrogatingPid, - interrogatingTid); + interactionId, callback, flags, interrogatingPid, interrogatingTid); + return getCompatibilityScale(resolvedWindowId); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId()."); @@ -1368,7 +1388,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return getCompatibilityScale(resolvedWindowId); + return 0; } @Override @@ -1377,8 +1397,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); - final int windowLeft; - final int windowTop; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { mSecurityPolicy.enforceCanRetrieveWindowContent(this); @@ -1392,19 +1410,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return 0; } } - IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId); - mWindowManagerService.getWindowFrame(token, mTempBounds); - windowLeft = mTempBounds.left; - windowTop = mTempBounds.top; } final int flags = (mIncludeNotImportantViews) ? AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, windowLeft, - windowTop, interactionId, callback, flags, interrogatingPid, + connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, + interactionId, callback, flags, interrogatingPid, interrogatingTid); + return getCompatibilityScale(resolvedWindowId); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()"); @@ -1412,7 +1427,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return getCompatibilityScale(resolvedWindowId); + return 0; } @Override @@ -1421,8 +1436,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnectionCallback callback, int flags, long interrogatingTid) throws RemoteException { final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); - final int windowLeft; - final int windowTop; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { mSecurityPolicy.enforceCanRetrieveWindowContent(this); @@ -1436,10 +1449,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return 0; } } - IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId); - mWindowManagerService.getWindowFrame(token, mTempBounds); - windowLeft = mTempBounds.left; - windowTop = mTempBounds.top; } final int allFlags = flags | ((mIncludeNotImportantViews) ? AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0); @@ -1447,8 +1456,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final long identityToken = Binder.clearCallingIdentity(); try { connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, - windowLeft, windowTop, interactionId, callback, allFlags, interrogatingPid, - interrogatingTid); + interactionId, callback, allFlags, interrogatingPid, interrogatingTid); + return getCompatibilityScale(resolvedWindowId); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); @@ -1456,7 +1465,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return getCompatibilityScale(resolvedWindowId); + return 0; } @Override @@ -1465,8 +1474,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); - final int windowLeft; - final int windowTop; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { mSecurityPolicy.enforceCanRetrieveWindowContent(this); @@ -1480,18 +1487,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return 0; } } - IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId); - mWindowManagerService.getWindowFrame(token, mTempBounds); - windowLeft = mTempBounds.left; - windowTop = mTempBounds.top; } final int flags = (mIncludeNotImportantViews) ? AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findFocus(accessibilityNodeId, focusType, windowLeft, windowTop, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + connection.findFocus(accessibilityNodeId, focusType, interactionId, callback, + flags, interrogatingPid, interrogatingTid); + return getCompatibilityScale(resolvedWindowId); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()"); @@ -1499,7 +1503,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return getCompatibilityScale(resolvedWindowId); + return 0; } @Override @@ -1508,8 +1512,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); - final int windowLeft; - final int windowTop; IAccessibilityInteractionConnection connection = null; synchronized (mLock) { mSecurityPolicy.enforceCanRetrieveWindowContent(this); @@ -1523,18 +1525,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return 0; } } - IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId); - mWindowManagerService.getWindowFrame(token, mTempBounds); - windowLeft = mTempBounds.left; - windowTop = mTempBounds.top; } final int flags = (mIncludeNotImportantViews) ? AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.focusSearch(accessibilityNodeId, direction, windowLeft, windowTop, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + connection.focusSearch(accessibilityNodeId, direction, interactionId, callback, + flags, interrogatingPid, interrogatingTid); + return getCompatibilityScale(resolvedWindowId); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()"); @@ -1542,7 +1541,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return getCompatibilityScale(resolvedWindowId); + return 0; } @Override @@ -1820,7 +1819,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private float getCompatibilityScale(int windowId) { IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId); - return mWindowManagerService.getWindowCompatibilityScale(windowToken); + try { + return mWindowManager.getWindowCompatibilityScale(windowToken); + } catch (RemoteException re) { + /* ignore */ + } + return 1.0f; } } @@ -1939,18 +1943,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private int getFocusedWindowId() { - // We call this only on window focus change or after touch - // exploration gesture end and the shown windows are not that - // many, so the linear look up is just fine. - IBinder token = mWindowManagerService.getFocusedWindowClientToken(); - if (token != null) { - SparseArray<IBinder> windows = mWindowIdToWindowTokenMap; - final int windowCount = windows.size(); - for (int i = 0; i < windowCount; i++) { - if (windows.valueAt(i) == token) { - return windows.keyAt(i); + final long identity = Binder.clearCallingIdentity(); + try { + // We call this only on window focus change or after touch + // exploration gesture end and the shown windows are not that + // many, so the linear look up is just fine. + IBinder token = mWindowManager.getFocusedWindowToken(); + if (token != null) { + SparseArray<IBinder> windows = mWindowIdToWindowTokenMap; + final int windowCount = windows.size(); + for (int i = 0; i < windowCount; i++) { + if (windows.valueAt(i) == token) { + return windows.keyAt(i); + } } } + } catch (RemoteException re) { + /* ignore */ + } finally { + Binder.restoreCallingIdentity(identity); } return -1; } diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java index 48c6b2a..ba9f2cd 100644 --- a/services/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/java/com/android/server/accessibility/TouchExplorer.java @@ -28,6 +28,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.SystemClock; import android.util.Slog; +import android.view.InputFilter; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; @@ -37,7 +38,6 @@ import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; import com.android.internal.R; -import com.android.server.input.InputFilter; import java.util.ArrayList; import java.util.Arrays; diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java new file mode 100644 index 0000000..ca7faa2 --- /dev/null +++ b/services/java/com/android/server/am/ActiveServices.java @@ -0,0 +1,2134 @@ +/* + * 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.am; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import com.android.internal.os.BatteryStatsImpl; +import com.android.server.am.ActivityManagerService.ItemMatcher; +import com.android.server.am.ActivityManagerService.NeededUriGrants; + +import android.app.ActivityManager; +import android.app.AppGlobals; +import android.app.IApplicationThread; +import android.app.IServiceConnection; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; +import android.os.Binder; +import android.os.IBinder; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +public class ActiveServices { + static final boolean DEBUG_SERVICE = ActivityManagerService.DEBUG_SERVICE; + static final boolean DEBUG_SERVICE_EXECUTING = ActivityManagerService.DEBUG_SERVICE_EXECUTING; + static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU; + static final String TAG = ActivityManagerService.TAG; + static final String TAG_MU = ActivityManagerService.TAG_MU; + + // How long we wait for a service to finish executing. + static final int SERVICE_TIMEOUT = 20*1000; + + // How long a service needs to be running until restarting its process + // is no longer considered to be a relaunch of the service. + static final int SERVICE_RESTART_DURATION = 5*1000; + + // How long a service needs to be running until it will start back at + // SERVICE_RESTART_DURATION after being killed. + static final int SERVICE_RESET_RUN_DURATION = 60*1000; + + // Multiplying factor to increase restart duration time by, for each time + // a service is killed before it has run for SERVICE_RESET_RUN_DURATION. + static final int SERVICE_RESTART_DURATION_FACTOR = 4; + + // The minimum amount of time between restarting services that we allow. + // That is, when multiple services are restarting, we won't allow each + // to restart less than this amount of time from the last one. + static final int SERVICE_MIN_RESTART_TIME_BETWEEN = 10*1000; + + // Maximum amount of time for there to be no activity on a service before + // we consider it non-essential and allow its process to go on the + // LRU background list. + static final int MAX_SERVICE_INACTIVITY = 30*60*1000; + + final ActivityManagerService mAm; + + final ServiceMap mServiceMap = new ServiceMap(); + + /** + * All currently bound service connections. Keys are the IBinder of + * the client's IServiceConnection. + */ + final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections + = new HashMap<IBinder, ArrayList<ConnectionRecord>>(); + + /** + * List of services that we have been asked to start, + * but haven't yet been able to. It is used to hold start requests + * while waiting for their corresponding application thread to get + * going. + */ + final ArrayList<ServiceRecord> mPendingServices + = new ArrayList<ServiceRecord>(); + + /** + * List of services that are scheduled to restart following a crash. + */ + final ArrayList<ServiceRecord> mRestartingServices + = new ArrayList<ServiceRecord>(); + + /** + * List of services that are in the process of being stopped. + */ + final ArrayList<ServiceRecord> mStoppingServices + = new ArrayList<ServiceRecord>(); + + static class ServiceMap { + + private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser + = new SparseArray<HashMap<ComponentName, ServiceRecord>>(); + private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>> + mServicesByIntentPerUser = new SparseArray< + HashMap<Intent.FilterComparison, ServiceRecord>>(); + + ServiceRecord getServiceByName(ComponentName name, int callingUser) { + // TODO: Deal with global services + if (DEBUG_MU) + Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser); + return getServices(callingUser).get(name); + } + + ServiceRecord getServiceByName(ComponentName name) { + return getServiceByName(name, -1); + } + + ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) { + // TODO: Deal with global services + if (DEBUG_MU) + Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser); + return getServicesByIntent(callingUser).get(filter); + } + + ServiceRecord getServiceByIntent(Intent.FilterComparison filter) { + return getServiceByIntent(filter, -1); + } + + void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) { + // TODO: Deal with global services + getServices(callingUser).put(name, value); + } + + void putServiceByIntent(Intent.FilterComparison filter, int callingUser, + ServiceRecord value) { + // TODO: Deal with global services + getServicesByIntent(callingUser).put(filter, value); + } + + void removeServiceByName(ComponentName name, int callingUser) { + // TODO: Deal with global services + ServiceRecord removed = getServices(callingUser).remove(name); + if (DEBUG_MU) + Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name + + " removed=" + removed); + } + + void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) { + // TODO: Deal with global services + ServiceRecord removed = getServicesByIntent(callingUser).remove(filter); + if (DEBUG_MU) + Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter + + " removed=" + removed); + } + + Collection<ServiceRecord> getAllServices(int callingUser) { + // TODO: Deal with global services + return getServices(callingUser).values(); + } + + private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) { + HashMap map = mServicesByNamePerUser.get(callingUser); + if (map == null) { + map = new HashMap<ComponentName, ServiceRecord>(); + mServicesByNamePerUser.put(callingUser, map); + } + return map; + } + + private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent( + int callingUser) { + HashMap map = mServicesByIntentPerUser.get(callingUser); + if (map == null) { + map = new HashMap<Intent.FilterComparison, ServiceRecord>(); + mServicesByIntentPerUser.put(callingUser, map); + } + return map; + } + } + + public ActiveServices(ActivityManagerService service) { + mAm = service; + } + + ComponentName startServiceLocked(IApplicationThread caller, + Intent service, String resolvedType, + int callingPid, int callingUid, int userId) { + if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service + + " type=" + resolvedType + " args=" + service.getExtras()); + + if (caller != null) { + final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); + if (callerApp == null) { + throw new SecurityException( + "Unable to find app for caller " + caller + + " (pid=" + Binder.getCallingPid() + + ") when starting service " + service); + } + } + + ServiceLookupResult res = + retrieveServiceLocked(service, resolvedType, + callingPid, callingUid, userId, true); + if (res == null) { + return null; + } + if (res.record == null) { + return new ComponentName("!", res.permission != null + ? res.permission : "private to package"); + } + ServiceRecord r = res.record; + NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked( + callingUid, r.packageName, service, service.getFlags(), null); + if (unscheduleServiceRestartLocked(r)) { + if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r); + } + r.startRequested = true; + r.callStart = false; + r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), + service, neededGrants)); + r.lastActivity = SystemClock.uptimeMillis(); + synchronized (r.stats.getBatteryStats()) { + r.stats.startRunningLocked(); + } + if (!bringUpServiceLocked(r, service.getFlags(), false)) { + return new ComponentName("!", "Service process is bad"); + } + return r.name; + } + + private void stopServiceLocked(ServiceRecord service) { + synchronized (service.stats.getBatteryStats()) { + service.stats.stopRunningLocked(); + } + service.startRequested = false; + service.callStart = false; + bringDownServiceLocked(service, false); + } + + int stopServiceLocked(IApplicationThread caller, Intent service, + String resolvedType, int userId) { + if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service + + " type=" + resolvedType); + + final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); + if (caller != null && callerApp == null) { + throw new SecurityException( + "Unable to find app for caller " + caller + + " (pid=" + Binder.getCallingPid() + + ") when stopping service " + service); + } + + // If this service is active, make sure it is stopped. + ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, + Binder.getCallingPid(), Binder.getCallingUid(), userId, false); + if (r != null) { + if (r.record != null) { + final long origId = Binder.clearCallingIdentity(); + try { + stopServiceLocked(r.record); + } finally { + Binder.restoreCallingIdentity(origId); + } + return 1; + } + return -1; + } + + return 0; + } + + IBinder peekServiceLocked(Intent service, String resolvedType) { + ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, + Binder.getCallingPid(), Binder.getCallingUid(), + UserHandle.getCallingUserId(), false); + + IBinder ret = null; + if (r != null) { + // r.record is null if findServiceLocked() failed the caller permission check + if (r.record == null) { + throw new SecurityException( + "Permission Denial: Accessing service " + r.record.name + + " from pid=" + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + r.permission); + } + IntentBindRecord ib = r.record.bindings.get(r.record.intent); + if (ib != null) { + ret = ib.binder; + } + } + + return ret; + } + + boolean stopServiceTokenLocked(ComponentName className, IBinder token, + int startId) { + if (DEBUG_SERVICE) Slog.v(TAG, "stopServiceToken: " + className + + " " + token + " startId=" + startId); + ServiceRecord r = findServiceLocked(className, token); + if (r != null) { + if (startId >= 0) { + // Asked to only stop if done with all work. Note that + // to avoid leaks, we will take this as dropping all + // start items up to and including this one. + ServiceRecord.StartItem si = r.findDeliveredStart(startId, false); + if (si != null) { + while (r.deliveredStarts.size() > 0) { + ServiceRecord.StartItem cur = r.deliveredStarts.remove(0); + cur.removeUriPermissionsLocked(); + if (cur == si) { + break; + } + } + } + + if (r.getLastStartId() != startId) { + return false; + } + + if (r.deliveredStarts.size() > 0) { + Slog.w(TAG, "stopServiceToken startId " + startId + + " is last, but have " + r.deliveredStarts.size() + + " remaining args"); + } + } + + synchronized (r.stats.getBatteryStats()) { + r.stats.stopRunningLocked(); + r.startRequested = false; + r.callStart = false; + } + final long origId = Binder.clearCallingIdentity(); + bringDownServiceLocked(r, false); + Binder.restoreCallingIdentity(origId); + return true; + } + return false; + } + + public void setServiceForegroundLocked(ComponentName className, IBinder token, + int id, Notification notification, boolean removeNotification) { + final long origId = Binder.clearCallingIdentity(); + try { + ServiceRecord r = findServiceLocked(className, token); + if (r != null) { + if (id != 0) { + if (notification == null) { + throw new IllegalArgumentException("null notification"); + } + if (r.foregroundId != id) { + r.cancelNotification(); + r.foregroundId = id; + } + notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; + r.foregroundNoti = notification; + r.isForeground = true; + r.postNotification(); + if (r.app != null) { + updateServiceForegroundLocked(r.app, true); + } + } else { + if (r.isForeground) { + r.isForeground = false; + if (r.app != null) { + mAm.updateLruProcessLocked(r.app, false, true); + updateServiceForegroundLocked(r.app, true); + } + } + if (removeNotification) { + r.cancelNotification(); + r.foregroundId = 0; + r.foregroundNoti = null; + } + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) { + boolean anyForeground = false; + for (ServiceRecord sr : proc.services) { + if (sr.isForeground) { + anyForeground = true; + break; + } + } + if (anyForeground != proc.foregroundServices) { + proc.foregroundServices = anyForeground; + if (oomAdj) { + mAm.updateOomAdjLocked(); + } + } + } + + int bindServiceLocked(IApplicationThread caller, IBinder token, + Intent service, String resolvedType, + IServiceConnection connection, int flags, int userId) { + if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service + + " type=" + resolvedType + " conn=" + connection.asBinder() + + " flags=0x" + Integer.toHexString(flags)); + if (DEBUG_MU) + Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid=" + + Binder.getOrigCallingUid()); + final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); + if (callerApp == null) { + throw new SecurityException( + "Unable to find app for caller " + caller + + " (pid=" + Binder.getCallingPid() + + ") when binding service " + service); + } + + ActivityRecord activity = null; + if (token != null) { + activity = mAm.mMainStack.isInStackLocked(token); + if (activity == null) { + Slog.w(TAG, "Binding with unknown activity: " + token); + return 0; + } + } + + int clientLabel = 0; + PendingIntent clientIntent = null; + + if (callerApp.info.uid == Process.SYSTEM_UID) { + // Hacky kind of thing -- allow system stuff to tell us + // what they are, so we can report this elsewhere for + // others to know why certain services are running. + try { + clientIntent = (PendingIntent)service.getParcelableExtra( + Intent.EXTRA_CLIENT_INTENT); + } catch (RuntimeException e) { + } + if (clientIntent != null) { + clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0); + if (clientLabel != 0) { + // There are no useful extras in the intent, trash them. + // System code calling with this stuff just needs to know + // this will happen. + service = service.cloneFilter(); + } + } + } + + ServiceLookupResult res = + retrieveServiceLocked(service, resolvedType, + Binder.getCallingPid(), Binder.getCallingUid(), userId, true); + if (res == null) { + return 0; + } + if (res.record == null) { + return -1; + } + if (mAm.isSingleton(res.record.processName, res.record.appInfo, + res.record.serviceInfo.name, res.record.serviceInfo.flags)) { + userId = 0; + res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(), + Binder.getCallingUid(), 0, true); + } + ServiceRecord s = res.record; + + final long origId = Binder.clearCallingIdentity(); + + try { + if (unscheduleServiceRestartLocked(s)) { + if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: " + + s); + } + + AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); + ConnectionRecord c = new ConnectionRecord(b, activity, + connection, flags, clientLabel, clientIntent); + + IBinder binder = connection.asBinder(); + ArrayList<ConnectionRecord> clist = s.connections.get(binder); + if (clist == null) { + clist = new ArrayList<ConnectionRecord>(); + s.connections.put(binder, clist); + } + clist.add(c); + b.connections.add(c); + if (activity != null) { + if (activity.connections == null) { + activity.connections = new HashSet<ConnectionRecord>(); + } + activity.connections.add(c); + } + b.client.connections.add(c); + if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { + b.client.hasAboveClient = true; + } + clist = mServiceConnections.get(binder); + if (clist == null) { + clist = new ArrayList<ConnectionRecord>(); + mServiceConnections.put(binder, clist); + } + clist.add(c); + + if ((flags&Context.BIND_AUTO_CREATE) != 0) { + s.lastActivity = SystemClock.uptimeMillis(); + if (!bringUpServiceLocked(s, service.getFlags(), false)) { + return 0; + } + } + + if (s.app != null) { + // This could have made the service more important. + mAm.updateOomAdjLocked(s.app); + } + + if (DEBUG_SERVICE) Slog.v(TAG, "Bind " + s + " with " + b + + ": received=" + b.intent.received + + " apps=" + b.intent.apps.size() + + " doRebind=" + b.intent.doRebind); + + if (s.app != null && b.intent.received) { + // Service is already running, so we can immediately + // publish the connection. + try { + c.conn.connected(s.name, b.intent.binder); + } catch (Exception e) { + Slog.w(TAG, "Failure sending service " + s.shortName + + " to connection " + c.conn.asBinder() + + " (in " + c.binding.client.processName + ")", e); + } + + // If this is the first app connected back to this binding, + // and the service had previously asked to be told when + // rebound, then do so. + if (b.intent.apps.size() == 1 && b.intent.doRebind) { + requestServiceBindingLocked(s, b.intent, true); + } + } else if (!b.intent.requested) { + requestServiceBindingLocked(s, b.intent, false); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + + return 1; + } + + void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { + final long origId = Binder.clearCallingIdentity(); + try { + if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r + + " " + intent + ": " + service); + if (r != null) { + Intent.FilterComparison filter + = new Intent.FilterComparison(intent); + IntentBindRecord b = r.bindings.get(filter); + if (b != null && !b.received) { + b.binder = service; + b.requested = true; + b.received = true; + if (r.connections.size() > 0) { + Iterator<ArrayList<ConnectionRecord>> it + = r.connections.values().iterator(); + while (it.hasNext()) { + ArrayList<ConnectionRecord> clist = it.next(); + for (int i=0; i<clist.size(); i++) { + ConnectionRecord c = clist.get(i); + if (!filter.equals(c.binding.intent.intent)) { + if (DEBUG_SERVICE) Slog.v( + TAG, "Not publishing to: " + c); + if (DEBUG_SERVICE) Slog.v( + TAG, "Bound intent: " + c.binding.intent.intent); + if (DEBUG_SERVICE) Slog.v( + TAG, "Published intent: " + intent); + continue; + } + if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c); + try { + c.conn.connected(r.name, service); + } catch (Exception e) { + Slog.w(TAG, "Failure sending service " + r.name + + " to connection " + c.conn.asBinder() + + " (in " + c.binding.client.processName + ")", e); + } + } + } + } + } + + serviceDoneExecutingLocked(r, mStoppingServices.contains(r)); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + boolean unbindServiceLocked(IServiceConnection connection) { + IBinder binder = connection.asBinder(); + if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder); + ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder); + if (clist == null) { + Slog.w(TAG, "Unbind failed: could not find connection for " + + connection.asBinder()); + return false; + } + + final long origId = Binder.clearCallingIdentity(); + try { + while (clist.size() > 0) { + ConnectionRecord r = clist.get(0); + removeConnectionLocked(r, null, null); + + if (r.binding.service.app != null) { + // This could have made the service less important. + mAm.updateOomAdjLocked(r.binding.service.app); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + + return true; + } + + void unbindFinishedLocked(ServiceRecord r, Intent intent, boolean doRebind) { + final long origId = Binder.clearCallingIdentity(); + try { + if (r != null) { + Intent.FilterComparison filter + = new Intent.FilterComparison(intent); + IntentBindRecord b = r.bindings.get(filter); + if (DEBUG_SERVICE) Slog.v(TAG, "unbindFinished in " + r + + " at " + b + ": apps=" + + (b != null ? b.apps.size() : 0)); + + boolean inStopping = mStoppingServices.contains(r); + if (b != null) { + if (b.apps.size() > 0 && !inStopping) { + // Applications have already bound since the last + // unbind, so just rebind right here. + requestServiceBindingLocked(r, b, true); + } else { + // Note to tell the service the next time there is + // a new client. + b.doRebind = true; + } + } + + serviceDoneExecutingLocked(r, inStopping); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + private final ServiceRecord findServiceLocked(ComponentName name, + IBinder token) { + ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser()); + return r == token ? r : null; + } + + private final class ServiceLookupResult { + final ServiceRecord record; + final String permission; + + ServiceLookupResult(ServiceRecord _record, String _permission) { + record = _record; + permission = _permission; + } + } + + private class ServiceRestarter implements Runnable { + private ServiceRecord mService; + + void setService(ServiceRecord service) { + mService = service; + } + + public void run() { + synchronized(mAm) { + performServiceRestartLocked(mService); + } + } + } + + private ServiceLookupResult retrieveServiceLocked(Intent service, + String resolvedType, int callingPid, int callingUid, int userId, + boolean createIfNeeded) { + ServiceRecord r = null; + if (DEBUG_SERVICE) Slog.v(TAG, "retrieveServiceLocked: " + service + + " type=" + resolvedType + " callingUid=" + callingUid); + + if (service.getComponent() != null) { + r = mServiceMap.getServiceByName(service.getComponent(), userId); + } + if (r == null) { + Intent.FilterComparison filter = new Intent.FilterComparison(service); + r = mServiceMap.getServiceByIntent(filter, userId); + } + if (r == null) { + try { + ResolveInfo rInfo = + AppGlobals.getPackageManager().resolveService( + service, resolvedType, + ActivityManagerService.STOCK_PM_FLAGS, userId); + ServiceInfo sInfo = + rInfo != null ? rInfo.serviceInfo : null; + if (sInfo == null) { + Slog.w(TAG, "Unable to start service " + service + " U=" + userId + + ": not found"); + return null; + } + ComponentName name = new ComponentName( + sInfo.applicationInfo.packageName, sInfo.name); + if (userId > 0) { + if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo, + sInfo.name, sInfo.flags)) { + userId = 0; + } + sInfo = new ServiceInfo(sInfo); + sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId); + } + r = mServiceMap.getServiceByName(name, userId); + if (r == null && createIfNeeded) { + Intent.FilterComparison filter = new Intent.FilterComparison( + service.cloneFilter()); + ServiceRestarter res = new ServiceRestarter(); + BatteryStatsImpl.Uid.Pkg.Serv ss = null; + BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + ss = stats.getServiceStatsLocked( + sInfo.applicationInfo.uid, sInfo.packageName, + sInfo.name); + } + r = new ServiceRecord(mAm, ss, name, filter, sInfo, res); + res.setService(r); + mServiceMap.putServiceByName(name, UserHandle.getUserId(r.appInfo.uid), r); + mServiceMap.putServiceByIntent(filter, UserHandle.getUserId(r.appInfo.uid), r); + + // Make sure this component isn't in the pending list. + int N = mPendingServices.size(); + for (int i=0; i<N; i++) { + ServiceRecord pr = mPendingServices.get(i); + if (pr.name.equals(name)) { + mPendingServices.remove(i); + i--; + N--; + } + } + } + } catch (RemoteException ex) { + // pm is in same process, this will never happen. + } + } + if (r != null) { + if (mAm.checkComponentPermission(r.permission, + callingPid, callingUid, r.appInfo.uid, r.exported) + != PackageManager.PERMISSION_GRANTED) { + if (!r.exported) { + Slog.w(TAG, "Permission Denial: Accessing service " + r.name + + " from pid=" + callingPid + + ", uid=" + callingUid + + " that is not exported from uid " + r.appInfo.uid); + return new ServiceLookupResult(null, "not exported from uid " + + r.appInfo.uid); + } + Slog.w(TAG, "Permission Denial: Accessing service " + r.name + + " from pid=" + callingPid + + ", uid=" + callingUid + + " requires " + r.permission); + return new ServiceLookupResult(null, r.permission); + } + return new ServiceLookupResult(r, null); + } + return null; + } + + private final void bumpServiceExecutingLocked(ServiceRecord r, String why) { + if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING " + + why + " of " + r + " in app " + r.app); + else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING " + + why + " of " + r.shortName); + long now = SystemClock.uptimeMillis(); + if (r.executeNesting == 0 && r.app != null) { + if (r.app.executingServices.size() == 0) { + Message msg = mAm.mHandler.obtainMessage( + ActivityManagerService.SERVICE_TIMEOUT_MSG); + msg.obj = r.app; + mAm.mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT); + } + r.app.executingServices.add(r); + } + r.executeNesting++; + r.executingStart = now; + } + + private final boolean requestServiceBindingLocked(ServiceRecord r, + IntentBindRecord i, boolean rebind) { + if (r.app == null || r.app.thread == null) { + // If service is not currently running, can't yet bind. + return false; + } + if ((!i.requested || rebind) && i.apps.size() > 0) { + try { + bumpServiceExecutingLocked(r, "bind"); + r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind); + if (!rebind) { + i.requested = true; + } + i.hasBound = true; + i.doRebind = false; + } catch (RemoteException e) { + if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r); + return false; + } + } + return true; + } + + private final boolean scheduleServiceRestartLocked(ServiceRecord r, + boolean allowCancel) { + boolean canceled = false; + + final long now = SystemClock.uptimeMillis(); + + if ((r.serviceInfo.applicationInfo.flags + &ApplicationInfo.FLAG_PERSISTENT) == 0) { + long minDuration = SERVICE_RESTART_DURATION; + long resetTime = SERVICE_RESET_RUN_DURATION; + + // Any delivered but not yet finished starts should be put back + // on the pending list. + final int N = r.deliveredStarts.size(); + if (N > 0) { + for (int i=N-1; i>=0; i--) { + ServiceRecord.StartItem si = r.deliveredStarts.get(i); + si.removeUriPermissionsLocked(); + if (si.intent == null) { + // We'll generate this again if needed. + } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT + && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) { + r.pendingStarts.add(0, si); + long dur = SystemClock.uptimeMillis() - si.deliveredTime; + dur *= 2; + if (minDuration < dur) minDuration = dur; + if (resetTime < dur) resetTime = dur; + } else { + Slog.w(TAG, "Canceling start item " + si.intent + " in service " + + r.name); + canceled = true; + } + } + r.deliveredStarts.clear(); + } + + r.totalRestartCount++; + if (r.restartDelay == 0) { + r.restartCount++; + r.restartDelay = minDuration; + } else { + // If it has been a "reasonably long time" since the service + // was started, then reset our restart duration back to + // the beginning, so we don't infinitely increase the duration + // on a service that just occasionally gets killed (which is + // a normal case, due to process being killed to reclaim memory). + if (now > (r.restartTime+resetTime)) { + r.restartCount = 1; + r.restartDelay = minDuration; + } else { + if ((r.serviceInfo.applicationInfo.flags + &ApplicationInfo.FLAG_PERSISTENT) != 0) { + // Services in peristent processes will restart much more + // quickly, since they are pretty important. (Think SystemUI). + r.restartDelay += minDuration/2; + } else { + r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR; + if (r.restartDelay < minDuration) { + r.restartDelay = minDuration; + } + } + } + } + + r.nextRestartTime = now + r.restartDelay; + + // Make sure that we don't end up restarting a bunch of services + // all at the same time. + boolean repeat; + do { + repeat = false; + for (int i=mRestartingServices.size()-1; i>=0; i--) { + ServiceRecord r2 = mRestartingServices.get(i); + if (r2 != r && r.nextRestartTime + >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN) + && r.nextRestartTime + < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) { + r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN; + r.restartDelay = r.nextRestartTime - now; + repeat = true; + break; + } + } + } while (repeat); + + } else { + // Persistent processes are immediately restrted, so there is no + // reason to hold of on restarting their services. + r.totalRestartCount++; + r.restartCount = 0; + r.restartDelay = 0; + r.nextRestartTime = now; + } + + if (!mRestartingServices.contains(r)) { + mRestartingServices.add(r); + } + + r.cancelNotification(); + + mAm.mHandler.removeCallbacks(r.restarter); + mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime); + r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; + Slog.w(TAG, "Scheduling restart of crashed service " + + r.shortName + " in " + r.restartDelay + "ms"); + EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, + r.shortName, r.restartDelay); + + return canceled; + } + + final void performServiceRestartLocked(ServiceRecord r) { + if (!mRestartingServices.contains(r)) { + return; + } + bringUpServiceLocked(r, r.intent.getIntent().getFlags(), true); + } + + private final boolean unscheduleServiceRestartLocked(ServiceRecord r) { + if (r.restartDelay == 0) { + return false; + } + r.resetRestartCounter(); + mRestartingServices.remove(r); + mAm.mHandler.removeCallbacks(r.restarter); + return true; + } + + private final boolean 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; + } + + if (!whileRestarting && r.restartDelay > 0) { + // If waiting for a restart, then do nothing. + return true; + } + + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent); + + // We are now bringing the service up, so no longer in the + // restarting state. + mRestartingServices.remove(r); + + // Service is now being launched, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + r.packageName, false, r.userId); + } catch (RemoteException e) { + } catch (IllegalArgumentException e) { + Slog.w(TAG, "Failed trying to unstop package " + + r.packageName + ": " + e); + } + + final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; + final String procName = r.processName; + ProcessRecord app; + + if (!isolated) { + app = mAm.getProcessRecordLocked(procName, r.appInfo.uid); + if (DEBUG_MU) + Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); + if (app != null && app.thread != null) { + try { + app.addPackage(r.appInfo.packageName); + realStartServiceLocked(r, app); + return true; + } catch (RemoteException e) { + Slog.w(TAG, "Exception when starting service " + r.shortName, e); + } + + // If a dead object exception was thrown -- fall through to + // restart the application. + } + } else { + // If this service runs in an isolated process, then each time + // we call startProcessLocked() we will get a new isolated + // process, starting another process if we are currently waiting + // for a previous process to come up. To deal with this, we store + // in the service any current isolated process it is running in or + // waiting to have come up. + app = r.isolatedProc; + } + + // Not running -- get it started, and enqueue this service record + // to be executed when the app comes up. + 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 " + + r.appInfo.packageName + "/" + + r.appInfo.uid + " for service " + + r.intent.getIntent() + ": process is bad"); + bringDownServiceLocked(r, true); + return false; + } + if (isolated) { + r.isolatedProc = app; + } + } + + if (!mPendingServices.contains(r)) { + mPendingServices.add(r); + } + + return true; + } + + private final void requestServiceBindingsLocked(ServiceRecord r) { + Iterator<IntentBindRecord> bindings = r.bindings.values().iterator(); + while (bindings.hasNext()) { + IntentBindRecord i = bindings.next(); + if (!requestServiceBindingLocked(r, i, false)) { + break; + } + } + } + + private final void realStartServiceLocked(ServiceRecord r, + ProcessRecord app) throws RemoteException { + if (app.thread == null) { + throw new RemoteException(); + } + if (DEBUG_MU) + Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid + + ", ProcessRecord.uid = " + app.uid); + r.app = app; + r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); + + app.services.add(r); + bumpServiceExecutingLocked(r, "create"); + mAm.updateLruProcessLocked(app, true, true); + + boolean created = false; + try { + mAm.mStringBuilder.setLength(0); + r.intent.getIntent().toShortString(mAm.mStringBuilder, true, false, true, false); + EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE, + System.identityHashCode(r), r.shortName, + mAm.mStringBuilder.toString(), r.app.pid); + synchronized (r.stats.getBatteryStats()) { + r.stats.startLaunchedLocked(); + } + mAm.ensurePackageDexOpt(r.serviceInfo.packageName); + app.thread.scheduleCreateService(r, r.serviceInfo, + mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo)); + r.postNotification(); + created = true; + } finally { + if (!created) { + app.services.remove(r); + scheduleServiceRestartLocked(r, false); + } + } + + requestServiceBindingsLocked(r); + + // If the service is in the started state, and there are no + // pending arguments, then fake up one so its onStartCommand() will + // be called. + if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { + r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), + null, null)); + } + + sendServiceArgsLocked(r, true); + } + + private final void sendServiceArgsLocked(ServiceRecord r, + boolean oomAdjusted) { + final int N = r.pendingStarts.size(); + if (N == 0) { + return; + } + + while (r.pendingStarts.size() > 0) { + try { + ServiceRecord.StartItem si = r.pendingStarts.remove(0); + if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: " + + r + " " + r.intent + " args=" + si.intent); + if (si.intent == null && N > 1) { + // If somehow we got a dummy null intent in the middle, + // then skip it. DO NOT skip a null intent when it is + // the only one in the list -- this is to support the + // onStartCommand(null) case. + continue; + } + si.deliveredTime = SystemClock.uptimeMillis(); + r.deliveredStarts.add(si); + si.deliveryCount++; + if (si.neededGrants != null) { + mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants, + si.getUriPermissionsLocked()); + } + bumpServiceExecutingLocked(r, "start"); + if (!oomAdjusted) { + oomAdjusted = true; + mAm.updateOomAdjLocked(r.app); + } + int flags = 0; + if (si.deliveryCount > 1) { + flags |= Service.START_FLAG_RETRY; + } + if (si.doneExecutingCount > 0) { + flags |= Service.START_FLAG_REDELIVERY; + } + r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); + } catch (RemoteException e) { + // Remote process gone... we'll let the normal cleanup take + // care of this. + if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r); + break; + } catch (Exception e) { + Slog.w(TAG, "Unexpected exception", e); + break; + } + } + } + + private final void bringDownServiceLocked(ServiceRecord r, boolean force) { + //Slog.i(TAG, "Bring down service:"); + //r.dump(" "); + + // Does it still need to run? + if (!force && r.startRequested) { + return; + } + if (r.connections.size() > 0) { + if (!force) { + // XXX should probably keep a count of the number of auto-create + // connections directly in the service. + Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator(); + while (it.hasNext()) { + ArrayList<ConnectionRecord> cr = it.next(); + for (int i=0; i<cr.size(); i++) { + if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) { + return; + } + } + } + } + + // Report to all of the connections that the service is no longer + // available. + Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator(); + while (it.hasNext()) { + ArrayList<ConnectionRecord> c = it.next(); + for (int i=0; i<c.size(); i++) { + ConnectionRecord cr = c.get(i); + // There is still a connection to the service that is + // being brought down. Mark it as dead. + cr.serviceDead = true; + try { + cr.conn.connected(r.name, null); + } catch (Exception e) { + Slog.w(TAG, "Failure disconnecting service " + r.name + + " to connection " + c.get(i).conn.asBinder() + + " (in " + c.get(i).binding.client.processName + ")", e); + } + } + } + } + + // Tell the service that it has been unbound. + if (r.bindings.size() > 0 && r.app != null && r.app.thread != null) { + Iterator<IntentBindRecord> it = r.bindings.values().iterator(); + while (it.hasNext()) { + IntentBindRecord ibr = it.next(); + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr + + ": hasBound=" + ibr.hasBound); + if (r.app != null && r.app.thread != null && ibr.hasBound) { + try { + bumpServiceExecutingLocked(r, "bring down unbind"); + mAm.updateOomAdjLocked(r.app); + ibr.hasBound = false; + r.app.thread.scheduleUnbindService(r, + ibr.intent.getIntent()); + } catch (Exception e) { + Slog.w(TAG, "Exception when unbinding service " + + r.shortName, e); + serviceDoneExecutingLocked(r, true); + } + } + } + } + + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent); + EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE, + System.identityHashCode(r), r.shortName, + (r.app != null) ? r.app.pid : -1); + + mServiceMap.removeServiceByName(r.name, r.userId); + mServiceMap.removeServiceByIntent(r.intent, r.userId); + r.totalRestartCount = 0; + unscheduleServiceRestartLocked(r); + + // Also make sure it is not on the pending list. + int N = mPendingServices.size(); + for (int i=0; i<N; i++) { + if (mPendingServices.get(i) == r) { + mPendingServices.remove(i); + if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r); + i--; + N--; + } + } + + r.cancelNotification(); + r.isForeground = false; + r.foregroundId = 0; + r.foregroundNoti = null; + + // Clear start entries. + r.clearDeliveredStartsLocked(); + r.pendingStarts.clear(); + + if (r.app != null) { + synchronized (r.stats.getBatteryStats()) { + r.stats.stopLaunchedLocked(); + } + r.app.services.remove(r); + if (r.app.thread != null) { + try { + bumpServiceExecutingLocked(r, "stop"); + mStoppingServices.add(r); + mAm.updateOomAdjLocked(r.app); + r.app.thread.scheduleStopService(r); + } catch (Exception e) { + Slog.w(TAG, "Exception when stopping service " + + r.shortName, e); + serviceDoneExecutingLocked(r, true); + } + updateServiceForegroundLocked(r.app, false); + } else { + if (DEBUG_SERVICE) Slog.v( + TAG, "Removed service that has no process: " + r); + } + } else { + if (DEBUG_SERVICE) Slog.v( + TAG, "Removed service that is not running: " + r); + } + + if (r.bindings.size() > 0) { + r.bindings.clear(); + } + + if (r.restarter instanceof ServiceRestarter) { + ((ServiceRestarter)r.restarter).setService(null); + } + } + + void removeConnectionLocked( + ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) { + IBinder binder = c.conn.asBinder(); + AppBindRecord b = c.binding; + ServiceRecord s = b.service; + ArrayList<ConnectionRecord> clist = s.connections.get(binder); + if (clist != null) { + clist.remove(c); + if (clist.size() == 0) { + s.connections.remove(binder); + } + } + b.connections.remove(c); + if (c.activity != null && c.activity != skipAct) { + if (c.activity.connections != null) { + c.activity.connections.remove(c); + } + } + if (b.client != skipApp) { + b.client.connections.remove(c); + if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { + b.client.updateHasAboveClientLocked(); + } + } + clist = mServiceConnections.get(binder); + if (clist != null) { + clist.remove(c); + if (clist.size() == 0) { + mServiceConnections.remove(binder); + } + } + + if (b.connections.size() == 0) { + b.intent.apps.remove(b.client); + } + + if (!c.serviceDead) { + if (DEBUG_SERVICE) Slog.v(TAG, "Disconnecting binding " + b.intent + + ": shouldUnbind=" + b.intent.hasBound); + if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 + && b.intent.hasBound) { + try { + bumpServiceExecutingLocked(s, "unbind"); + mAm.updateOomAdjLocked(s.app); + b.intent.hasBound = false; + // Assume the client doesn't want to know about a rebind; + // we will deal with that later if it asks for one. + b.intent.doRebind = false; + s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent()); + } catch (Exception e) { + Slog.w(TAG, "Exception when unbinding service " + s.shortName, e); + serviceDoneExecutingLocked(s, true); + } + } + + if ((c.flags&Context.BIND_AUTO_CREATE) != 0) { + bringDownServiceLocked(s, false); + } + } + } + + void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) { + boolean inStopping = mStoppingServices.contains(r); + if (r != null) { + if (type == 1) { + // This is a call from a service start... take care of + // book-keeping. + r.callStart = true; + switch (res) { + case Service.START_STICKY_COMPATIBILITY: + case Service.START_STICKY: { + // We are done with the associated start arguments. + r.findDeliveredStart(startId, true); + // Don't stop if killed. + r.stopIfKilled = false; + break; + } + case Service.START_NOT_STICKY: { + // We are done with the associated start arguments. + r.findDeliveredStart(startId, true); + if (r.getLastStartId() == startId) { + // There is no more work, and this service + // doesn't want to hang around if killed. + r.stopIfKilled = true; + } + break; + } + case Service.START_REDELIVER_INTENT: { + // We'll keep this item until they explicitly + // call stop for it, but keep track of the fact + // that it was delivered. + ServiceRecord.StartItem si = r.findDeliveredStart(startId, false); + if (si != null) { + si.deliveryCount = 0; + si.doneExecutingCount++; + // Don't stop if killed. + r.stopIfKilled = true; + } + break; + } + case Service.START_TASK_REMOVED_COMPLETE: { + // Special processing for onTaskRemoved(). Don't + // impact normal onStartCommand() processing. + r.findDeliveredStart(startId, true); + break; + } + default: + throw new IllegalArgumentException( + "Unknown service start result: " + res); + } + if (res == Service.START_STICKY_COMPATIBILITY) { + r.callStart = false; + } + } + if (DEBUG_MU) + Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid=" + + Binder.getOrigCallingUid()); + final long origId = Binder.clearCallingIdentity(); + serviceDoneExecutingLocked(r, inStopping); + Binder.restoreCallingIdentity(origId); + } else { + Slog.w(TAG, "Done executing unknown service from pid " + + Binder.getCallingPid()); + } + } + + private void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) { + if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r + + ": nesting=" + r.executeNesting + + ", inStopping=" + inStopping + ", app=" + r.app); + else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName); + r.executeNesting--; + if (r.executeNesting <= 0 && r.app != null) { + if (DEBUG_SERVICE) Slog.v(TAG, + "Nesting at 0 of " + r.shortName); + r.app.executingServices.remove(r); + if (r.app.executingServices.size() == 0) { + if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG, + "No more executingServices of " + r.shortName); + mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app); + } + if (inStopping) { + if (DEBUG_SERVICE) Slog.v(TAG, + "doneExecuting remove stopping " + r); + mStoppingServices.remove(r); + r.bindings.clear(); + } + mAm.updateOomAdjLocked(r.app); + } + } + + boolean attachApplicationLocked(ProcessRecord proc, String processName) throws Exception { + boolean didSomething = false; + // Collect any services that are waiting for this process to come up. + if (mPendingServices.size() > 0) { + ServiceRecord sr = null; + try { + for (int i=0; i<mPendingServices.size(); i++) { + sr = mPendingServices.get(i); + if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid + || !processName.equals(sr.processName))) { + continue; + } + + mPendingServices.remove(i); + i--; + realStartServiceLocked(sr, proc); + didSomething = true; + } + } catch (Exception e) { + Slog.w(TAG, "Exception in new application when starting service " + + sr.shortName, e); + throw e; + } + } + // Also, if there are any services that are waiting to restart and + // would run in this process, now is a good time to start them. It would + // be weird to bring up the process but arbitrarily not let the services + // run at this point just because their restart time hasn't come up. + if (mRestartingServices.size() > 0) { + ServiceRecord sr = null; + for (int i=0; i<mRestartingServices.size(); i++) { + sr = mRestartingServices.get(i); + if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid + || !processName.equals(sr.processName))) { + continue; + } + mAm.mHandler.removeCallbacks(sr.restarter); + mAm.mHandler.post(sr.restarter); + } + } + return didSomething; + } + + void processStartTimedOutLocked(ProcessRecord proc) { + for (int i=0; i<mPendingServices.size(); i++) { + ServiceRecord sr = mPendingServices.get(i); + if ((proc.uid == sr.appInfo.uid + && proc.processName.equals(sr.processName)) + || sr.isolatedProc == proc) { + Slog.w(TAG, "Forcing bringing down service: " + sr); + sr.isolatedProc = null; + mPendingServices.remove(i); + i--; + bringDownServiceLocked(sr, true); + } + } + } + + boolean forceStopLocked(String name, int userId, boolean evenPersistent, boolean doit) { + boolean didSomething = false; + ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); + for (ServiceRecord service : mServiceMap.getAllServices(userId)) { + if (service.packageName.equals(name) + && (service.app == null || evenPersistent || !service.app.persistent)) { + if (!doit) { + return true; + } + didSomething = true; + Slog.i(TAG, " Force stopping service " + service); + if (service.app != null) { + service.app.removed = true; + } + service.app = null; + service.isolatedProc = null; + services.add(service); + } + } + + int N = services.size(); + for (int i=0; i<N; i++) { + bringDownServiceLocked(services.get(i), true); + } + return didSomething; + } + + void cleanUpRemovedTaskLocked(TaskRecord tr, ComponentName component, Intent baseIntent) { + ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); + for (ServiceRecord sr : mServiceMap.getAllServices(tr.userId)) { + if (sr.packageName.equals(component.getPackageName())) { + services.add(sr); + } + } + + // Take care of any running services associated with the app. + for (int i=0; i<services.size(); i++) { + ServiceRecord sr = services.get(i); + if (sr.startRequested) { + if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) { + Slog.i(TAG, "Stopping service " + sr.shortName + ": remove task"); + stopServiceLocked(sr); + } else { + sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, + sr.makeNextStartId(), baseIntent, null)); + if (sr.app != null && sr.app.thread != null) { + sendServiceArgsLocked(sr, false); + } + } + } + } + } + + final void killServicesLocked(ProcessRecord app, + boolean allowRestart) { + // Report disconnected services. + if (false) { + // XXX we are letting the client link to the service for + // death notifications. + if (app.services.size() > 0) { + Iterator<ServiceRecord> it = app.services.iterator(); + while (it.hasNext()) { + ServiceRecord r = it.next(); + if (r.connections.size() > 0) { + Iterator<ArrayList<ConnectionRecord>> jt + = r.connections.values().iterator(); + while (jt.hasNext()) { + ArrayList<ConnectionRecord> cl = jt.next(); + for (int i=0; i<cl.size(); i++) { + ConnectionRecord c = cl.get(i); + if (c.binding.client != app) { + try { + //c.conn.connected(r.className, null); + } catch (Exception e) { + // todo: this should be asynchronous! + Slog.w(TAG, "Exception thrown disconnected servce " + + r.shortName + + " from app " + app.processName, e); + } + } + } + } + } + } + } + } + + // Clean up any connections this application has to other services. + if (app.connections.size() > 0) { + Iterator<ConnectionRecord> it = app.connections.iterator(); + while (it.hasNext()) { + ConnectionRecord r = it.next(); + removeConnectionLocked(r, app, null); + } + } + app.connections.clear(); + + if (app.services.size() != 0) { + // Any services running in the application need to be placed + // back in the pending list. + Iterator<ServiceRecord> it = app.services.iterator(); + while (it.hasNext()) { + ServiceRecord sr = it.next(); + synchronized (sr.stats.getBatteryStats()) { + sr.stats.stopLaunchedLocked(); + } + sr.app = null; + sr.isolatedProc = null; + sr.executeNesting = 0; + if (mStoppingServices.remove(sr)) { + if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); + } + + boolean hasClients = sr.bindings.size() > 0; + if (hasClients) { + Iterator<IntentBindRecord> bindings + = sr.bindings.values().iterator(); + while (bindings.hasNext()) { + IntentBindRecord b = bindings.next(); + if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b + + ": shouldUnbind=" + b.hasBound); + b.binder = null; + b.requested = b.received = b.hasBound = false; + } + } + + if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags + &ApplicationInfo.FLAG_PERSISTENT) == 0) { + Slog.w(TAG, "Service crashed " + sr.crashCount + + " times, stopping: " + sr); + EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, + sr.crashCount, sr.shortName, app.pid); + bringDownServiceLocked(sr, true); + } else if (!allowRestart) { + bringDownServiceLocked(sr, true); + } else { + boolean canceled = scheduleServiceRestartLocked(sr, true); + + // Should the service remain running? Note that in the + // extreme case of so many attempts to deliver a command + // that it failed we also will stop it here. + if (sr.startRequested && (sr.stopIfKilled || canceled)) { + if (sr.pendingStarts.size() == 0) { + sr.startRequested = false; + if (!hasClients) { + // Whoops, no reason to restart! + bringDownServiceLocked(sr, true); + } + } + } + } + } + + if (!allowRestart) { + app.services.clear(); + } + } + + // Make sure we have no more records on the stopping list. + int i = mStoppingServices.size(); + while (i > 0) { + i--; + ServiceRecord sr = mStoppingServices.get(i); + if (sr.app == app) { + mStoppingServices.remove(i); + if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); + } + } + + app.executingServices.clear(); + } + + ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) { + ActivityManager.RunningServiceInfo info = + new ActivityManager.RunningServiceInfo(); + info.service = r.name; + if (r.app != null) { + info.pid = r.app.pid; + } + info.uid = r.appInfo.uid; + info.process = r.processName; + info.foreground = r.isForeground; + info.activeSince = r.createTime; + info.started = r.startRequested; + info.clientCount = r.connections.size(); + info.crashCount = r.crashCount; + info.lastActivityTime = r.lastActivity; + if (r.isForeground) { + info.flags |= ActivityManager.RunningServiceInfo.FLAG_FOREGROUND; + } + if (r.startRequested) { + info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED; + } + if (r.app != null && r.app.pid == ActivityManagerService.MY_PID) { + info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS; + } + if (r.app != null && r.app.persistent) { + info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS; + } + + for (ArrayList<ConnectionRecord> connl : r.connections.values()) { + for (int i=0; i<connl.size(); i++) { + ConnectionRecord conn = connl.get(i); + if (conn.clientLabel != 0) { + info.clientPackage = conn.binding.client.info.packageName; + info.clientLabel = conn.clientLabel; + return info; + } + } + } + return info; + } + + List<ActivityManager.RunningServiceInfo> getRunningServiceInfoLocked(int maxNum, + int flags) { + ArrayList<ActivityManager.RunningServiceInfo> res + = new ArrayList<ActivityManager.RunningServiceInfo>(); + + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + if (ActivityManager.checkUidPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + uid) == PackageManager.PERMISSION_GRANTED) { + List<UserInfo> users = mAm.getUserManager().getUsers(); + for (int ui=0; ui<users.size() && res.size() < maxNum; ui++) { + final UserInfo user = users.get(ui); + if (mServiceMap.getAllServices(user.id).size() > 0) { + Iterator<ServiceRecord> it = mServiceMap.getAllServices( + user.id).iterator(); + while (it.hasNext() && res.size() < maxNum) { + res.add(makeRunningServiceInfoLocked(it.next())); + } + } + } + + for (int i=0; i<mRestartingServices.size() && res.size() < maxNum; i++) { + ServiceRecord r = mRestartingServices.get(i); + ActivityManager.RunningServiceInfo info = + makeRunningServiceInfoLocked(r); + info.restarting = r.nextRestartTime; + res.add(info); + } + } else { + int userId = UserHandle.getUserId(uid); + if (mServiceMap.getAllServices(userId).size() > 0) { + Iterator<ServiceRecord> it + = mServiceMap.getAllServices(userId).iterator(); + while (it.hasNext() && res.size() < maxNum) { + res.add(makeRunningServiceInfoLocked(it.next())); + } + } + + for (int i=0; i<mRestartingServices.size() && res.size() < maxNum; i++) { + ServiceRecord r = mRestartingServices.get(i); + if (r.userId == userId) { + ActivityManager.RunningServiceInfo info = + makeRunningServiceInfoLocked(r); + info.restarting = r.nextRestartTime; + res.add(info); + } + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + + return res; + } + + public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) { + int userId = UserHandle.getUserId(Binder.getCallingUid()); + ServiceRecord r = mServiceMap.getServiceByName(name, userId); + if (r != null) { + for (ArrayList<ConnectionRecord> conn : r.connections.values()) { + for (int i=0; i<conn.size(); i++) { + if (conn.get(i).clientIntent != null) { + return conn.get(i).clientIntent; + } + } + } + } + return null; + } + + void serviceTimeout(ProcessRecord proc) { + String anrMessage = null; + + synchronized(this) { + if (proc.executingServices.size() == 0 || proc.thread == null) { + return; + } + long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT; + Iterator<ServiceRecord> it = proc.executingServices.iterator(); + ServiceRecord timeout = null; + long nextTime = 0; + while (it.hasNext()) { + ServiceRecord sr = it.next(); + if (sr.executingStart < maxTime) { + timeout = sr; + break; + } + if (sr.executingStart > nextTime) { + nextTime = sr.executingStart; + } + } + if (timeout != null && mAm.mLruProcesses.contains(proc)) { + Slog.w(TAG, "Timeout executing service: " + timeout); + anrMessage = "Executing service " + timeout.shortName; + } else { + Message msg = mAm.mHandler.obtainMessage( + ActivityManagerService.SERVICE_TIMEOUT_MSG); + msg.obj = proc; + mAm.mHandler.sendMessageAtTime(msg, nextTime+SERVICE_TIMEOUT); + } + } + + if (anrMessage != null) { + mAm.appNotResponding(proc, null, null, anrMessage); + } + } + + /** + * Prints a list of ServiceRecords (dumpsys activity services) + */ + boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { + boolean needSep = false; + + ItemMatcher matcher = new ItemMatcher(); + matcher.build(args, opti); + + pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)"); + try { + List<UserInfo> users = mAm.getUserManager().getUsers(); + for (int ui=0; ui<users.size(); ui++) { + final UserInfo user = users.get(ui); + if (mServiceMap.getAllServices(user.id).size() > 0) { + boolean printed = false; + long nowReal = SystemClock.elapsedRealtime(); + Iterator<ServiceRecord> it = mServiceMap.getAllServices( + user.id).iterator(); + needSep = false; + while (it.hasNext()) { + ServiceRecord r = it.next(); + if (!matcher.match(r, r.name)) { + continue; + } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (ui > 0) { + pw.println(); + } + pw.println(" User " + user.id + " active services:"); + printed = true; + } + if (needSep) { + pw.println(); + } + pw.print(" * "); + pw.println(r); + if (dumpAll) { + r.dump(pw, " "); + needSep = true; + } else { + pw.print(" app="); + pw.println(r.app); + pw.print(" created="); + TimeUtils.formatDuration(r.createTime, nowReal, pw); + pw.print(" started="); + pw.print(r.startRequested); + pw.print(" connections="); + pw.println(r.connections.size()); + if (r.connections.size() > 0) { + pw.println(" Connections:"); + for (ArrayList<ConnectionRecord> clist : r.connections.values()) { + for (int i = 0; i < clist.size(); i++) { + ConnectionRecord conn = clist.get(i); + pw.print(" "); + pw.print(conn.binding.intent.intent.getIntent() + .toShortString(false, false, false, false)); + pw.print(" -> "); + ProcessRecord proc = conn.binding.client; + pw.println(proc != null ? proc.toShortString() : "null"); + } + } + } + } + if (dumpClient && r.app != null && r.app.thread != null) { + pw.println(" Client:"); + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); + try { + r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), + r, args); + tp.setBufferPrefix(" "); + // Short timeout, since blocking here can + // deadlock with the application. + tp.go(fd, 2000); + } finally { + tp.kill(); + } + } catch (IOException e) { + pw.println(" Failure while dumping the service: " + e); + } catch (RemoteException e) { + pw.println(" Got a RemoteException while dumping the service"); + } + needSep = true; + } + } + needSep = printed; + } + } + } catch (Exception e) { + Log.w(TAG, "Exception in dumpServicesLocked: " + e); + } + + if (mPendingServices.size() > 0) { + boolean printed = false; + for (int i=0; i<mPendingServices.size(); i++) { + ServiceRecord r = mPendingServices.get(i); + if (!matcher.match(r, r.name)) { + continue; + } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Pending services:"); + printed = true; + } + pw.print(" * Pending "); pw.println(r); + r.dump(pw, " "); + } + needSep = true; + } + + if (mRestartingServices.size() > 0) { + boolean printed = false; + for (int i=0; i<mRestartingServices.size(); i++) { + ServiceRecord r = mRestartingServices.get(i); + if (!matcher.match(r, r.name)) { + continue; + } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Restarting services:"); + printed = true; + } + pw.print(" * Restarting "); pw.println(r); + r.dump(pw, " "); + } + needSep = true; + } + + if (mStoppingServices.size() > 0) { + boolean printed = false; + for (int i=0; i<mStoppingServices.size(); i++) { + ServiceRecord r = mStoppingServices.get(i); + if (!matcher.match(r, r.name)) { + continue; + } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Stopping services:"); + printed = true; + } + pw.print(" * Stopping "); pw.println(r); + r.dump(pw, " "); + } + needSep = true; + } + + if (dumpAll) { + if (mServiceConnections.size() > 0) { + boolean printed = false; + Iterator<ArrayList<ConnectionRecord>> it + = mServiceConnections.values().iterator(); + while (it.hasNext()) { + ArrayList<ConnectionRecord> r = it.next(); + for (int i=0; i<r.size(); i++) { + ConnectionRecord cr = r.get(i); + if (!matcher.match(cr.binding.service, cr.binding.service.name)) { + continue; + } + if (dumpPackage != null && (cr.binding.client == null + || !dumpPackage.equals(cr.binding.client.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Connection bindings to services:"); + printed = true; + } + pw.print(" * "); pw.println(cr); + cr.dump(pw, " "); + } + } + needSep = true; + } + } + + return needSep; + } + + /** + * There are three ways to call this: + * - no service specified: dump all the services + * - a flattened component name that matched an existing service was specified as the + * first arg: dump that one service + * - the first arg isn't the flattened component name of an existing service: + * dump all services whose component contains the first arg as a substring + */ + protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args, + int opti, boolean dumpAll) { + ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); + + List<UserInfo> users = mAm.getUserManager().getUsers(); + if ("all".equals(name)) { + synchronized (this) { + for (UserInfo user : users) { + for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) { + services.add(r1); + } + } + } + } else { + ComponentName componentName = name != null + ? ComponentName.unflattenFromString(name) : null; + int objectId = 0; + if (componentName == null) { + // Not a '/' separated full component name; maybe an object ID? + try { + objectId = Integer.parseInt(name, 16); + name = null; + componentName = null; + } catch (RuntimeException e) { + } + } + + synchronized (this) { + for (UserInfo user : users) { + for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) { + if (componentName != null) { + if (r1.name.equals(componentName)) { + services.add(r1); + } + } else if (name != null) { + if (r1.name.flattenToString().contains(name)) { + services.add(r1); + } + } else if (System.identityHashCode(r1) == objectId) { + services.add(r1); + } + } + } + } + } + + if (services.size() <= 0) { + return false; + } + + boolean needSep = false; + for (int i=0; i<services.size(); i++) { + if (needSep) { + pw.println(); + } + needSep = true; + dumpService("", fd, pw, services.get(i), args, dumpAll); + } + return true; + } + + /** + * Invokes IApplicationThread.dumpService() on the thread of the specified service if + * there is a thread associated with the service. + */ + private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw, + final ServiceRecord r, String[] args, boolean dumpAll) { + String innerPrefix = prefix + " "; + synchronized (this) { + pw.print(prefix); pw.print("SERVICE "); + pw.print(r.shortName); pw.print(" "); + pw.print(Integer.toHexString(System.identityHashCode(r))); + pw.print(" pid="); + if (r.app != null) pw.println(r.app.pid); + else pw.println("(not running)"); + if (dumpAll) { + r.dump(pw, innerPrefix); + } + } + if (r.app != null && r.app.thread != null) { + pw.print(prefix); pw.println(" Client:"); + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); + try { + r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args); + tp.setBufferPrefix(prefix + " "); + tp.go(fd); + } finally { + tp.kill(); + } + } catch (IOException e) { + pw.println(prefix + " Failure while dumping the service: " + e); + } catch (RemoteException e) { + pw.println(prefix + " Got a RemoteException while dumping the service"); + } + } + } + +} diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 73deef0..2b4f8b1 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -51,7 +51,6 @@ import android.app.Instrumentation; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.Service; import android.app.backup.IBackupManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -76,12 +75,12 @@ import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PathPermission; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.content.pm.UserInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -111,7 +110,8 @@ import android.os.ServiceManager; import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UserId; +import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.text.format.Time; import android.util.EventLog; @@ -144,7 +144,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -242,31 +241,6 @@ public final class ActivityManagerService extends ActivityManagerNative static final int BROADCAST_FG_TIMEOUT = 10*1000; static final int BROADCAST_BG_TIMEOUT = 60*1000; - // How long we wait for a service to finish executing. - static final int SERVICE_TIMEOUT = 20*1000; - - // How long a service needs to be running until restarting its process - // is no longer considered to be a relaunch of the service. - static final int SERVICE_RESTART_DURATION = 5*1000; - - // How long a service needs to be running until it will start back at - // SERVICE_RESTART_DURATION after being killed. - static final int SERVICE_RESET_RUN_DURATION = 60*1000; - - // Multiplying factor to increase restart duration time by, for each time - // a service is killed before it has run for SERVICE_RESET_RUN_DURATION. - static final int SERVICE_RESTART_DURATION_FACTOR = 4; - - // The minimum amount of time between restarting services that we allow. - // That is, when multiple services are restarting, we won't allow each - // to restart less than this amount of time from the last one. - static final int SERVICE_MIN_RESTART_TIME_BETWEEN = 10*1000; - - // Maximum amount of time for there to be no activity on a service before - // we consider it non-essential and allow its process to go on the - // LRU background list. - static final int MAX_SERVICE_INACTIVITY = 30*60*1000; - // How long we wait until we timeout on key dispatching. static final int KEY_DISPATCHING_TIMEOUT = 5*1000; @@ -514,6 +488,11 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + protected BroadcastFilter[] newArray(int size) { + return new BroadcastFilter[size]; + } + + @Override protected String packageForFilter(BroadcastFilter filter) { return filter.packageName; } @@ -527,35 +506,7 @@ public final class ActivityManagerService extends ActivityManagerNative final HashMap<String, ArrayList<Intent>> mStickyBroadcasts = new HashMap<String, ArrayList<Intent>>(); - final ServiceMap mServiceMap = new ServiceMap(); - - /** - * All currently bound service connections. Keys are the IBinder of - * the client's IServiceConnection. - */ - final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections - = new HashMap<IBinder, ArrayList<ConnectionRecord>>(); - - /** - * List of services that we have been asked to start, - * but haven't yet been able to. It is used to hold start requests - * while waiting for their corresponding application thread to get - * going. - */ - final ArrayList<ServiceRecord> mPendingServices - = new ArrayList<ServiceRecord>(); - - /** - * List of services that are scheduled to restart following a crash. - */ - final ArrayList<ServiceRecord> mRestartingServices - = new ArrayList<ServiceRecord>(); - - /** - * List of services that are in the process of being stopped. - */ - final ArrayList<ServiceRecord> mStoppingServices - = new ArrayList<ServiceRecord>(); + final ActiveServices mServices; /** * Backup/restore process management @@ -733,6 +684,18 @@ public final class ActivityManagerService extends ActivityManagerNative int mLruSeq = 0; /** + * Keep track of the non-hidden/empty process we last found, to help + * determine how to distribute hidden/empty processes next time. + */ + int mNumNonHiddenProcs = 0; + + /** + * Keep track of the number of hidden procs, to balance oom adj + * distribution between those and empty procs. + */ + int mNumHiddenProcs = 0; + + /** * Keep track of the number of service processes we last found, to * determine on the next iteration which should be B services. */ @@ -827,6 +790,10 @@ public final class ActivityManagerService extends ActivityManagerNative static ActivityManagerService mSelf; static ActivityThread mSystemThread; + private int mCurrentUserId; + private SparseIntArray mLoggedInUsers = new SparseIntArray(5); + private UserManager mUserManager; + private final class AppDeathRecipient implements IBinder.DeathRecipient { final ProcessRecord mApp; final int mPid; @@ -1010,10 +977,10 @@ public final class ActivityManagerService extends ActivityManagerNative mDidDexOpt = false; Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); nmsg.obj = msg.obj; - mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT); + mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT); return; } - serviceTimeout((ProcessRecord)msg.obj); + mServices.serviceTimeout((ProcessRecord)msg.obj); } break; case UPDATE_TIME_ZONE: { synchronized (ActivityManagerService.this) { @@ -1115,7 +1082,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean restart = (msg.arg2 == 1); String pkg = (String) msg.obj; forceStopPackageLocked(pkg, uid, restart, false, true, false, - UserId.getUserId(uid)); + UserHandle.getUserId(uid)); } } break; case FINALIZE_PENDING_INTENT_MSG: { @@ -1284,7 +1251,8 @@ public final class ActivityManagerService extends ActivityManagerNative catPw.println(); dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); catPw.println(); - dumpServicesLocked(null, catPw, emptyArgs, 0, false, false, null); + mServices.dumpServicesLocked(null, catPw, emptyArgs, 0, + false, false, null); catPw.println(); dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); } @@ -1521,6 +1489,8 @@ public final class ActivityManagerService extends ActivityManagerNative mBroadcastQueues[0] = mFgBroadcastQueue; mBroadcastQueues[1] = mBgBroadcastQueue; + mServices = new ActiveServices(this); + File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); systemDir.mkdirs(); @@ -1857,7 +1827,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (procs == null) return null; final int N = procs.size(); for (int i = 0; i < N; i++) { - if (UserId.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i); + if (UserHandle.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i); } } ProcessRecord proc = mProcessNames.get(processName, uid); @@ -1992,7 +1962,7 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(app.pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } - app.pid = 0; + app.setPid(0); } if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG, @@ -2008,13 +1978,18 @@ public final class ActivityManagerService extends ActivityManagerNative int uid = app.uid; int[] gids = null; + int mountExternal = Zygote.MOUNT_EXTERNAL_NONE; if (!app.isolated) { try { - gids = mContext.getPackageManager().getPackageGids( - app.info.packageName); + final PackageManager pm = mContext.getPackageManager(); + gids = pm.getPackageGids(app.info.packageName); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Unable to retrieve gids", e); } + + if (Environment.isExternalStorageEmulated()) { + mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER; + } } if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) { if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL @@ -2053,7 +2028,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", - app.processName, uid, uid, gids, debugFlags, + app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, null, null); BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); @@ -2095,7 +2070,7 @@ public final class ActivityManagerService extends ActivityManagerNative } buf.append("}"); Slog.i(TAG, buf.toString()); - app.pid = startResult.pid; + app.setPid(startResult.pid); app.usingWrapper = startResult.usingWrapper; app.removed = false; synchronized (mPidsSelfLocked) { @@ -2107,7 +2082,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } catch (RuntimeException e) { // XXX do better error recovery. - app.pid = 0; + app.setPid(0); Slog.e(TAG, "Failure starting process " + app.processName, e); } } @@ -2223,7 +2198,7 @@ public final class ActivityManagerService extends ActivityManagerNative } void enforceNotIsolatedCaller(String caller) { - if (UserId.isIsolated(Binder.getCallingUid())) { + if (UserHandle.isIsolated(Binder.getCallingUid())) { throw new SecurityException("Isolated process not allowed to call " + caller); } } @@ -2351,19 +2326,48 @@ public final class ActivityManagerService extends ActivityManagerNative Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) { + return startActivityAsUser(caller, intent, resolvedType, resultTo, resultWho, requestCode, + startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId()); + } + + public final int startActivityAsUser(IApplicationThread caller, + Intent intent, String resolvedType, IBinder resultTo, + String resultWho, int requestCode, int startFlags, + String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) { enforceNotIsolatedCaller("startActivity"); - int userId = 0; - if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) { - // Requesting home, set the identity to the current user - // HACK! - userId = mCurrentUserId; + if (userId != UserHandle.getCallingUserId()) { + // Requesting a different user, make sure that they have the permission + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Binder.getCallingPid(), Binder.getCallingUid(), -1, true) + == PackageManager.PERMISSION_GRANTED) { + // Translate to the current user id, if caller wasn't aware + if (userId == UserHandle.USER_CURRENT) { + userId = mCurrentUserId; + } + } else { + String msg = "Permission Denial: " + + "Request to startActivity as user " + userId + + " but is calling from user " + UserHandle.getCallingUserId() + + "; this requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } } else { - // TODO: Fix this in a better way - calls coming from SystemUI should probably carry - // the current user's userId - if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) { - userId = 0; + if (intent.getCategories() != null + && intent.getCategories().contains(Intent.CATEGORY_HOME)) { + // Requesting home, set the identity to the current user + // HACK! + userId = mCurrentUserId; } else { - userId = Binder.getOrigCallingUser(); + // TODO: Fix this in a better way - calls coming from SystemUI should probably carry + // the current user's userId + if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) { + userId = 0; + } else { + userId = Binder.getOrigCallingUser(); + } } } return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType, @@ -2456,7 +2460,7 @@ public final class ActivityManagerService extends ActivityManagerNative AppGlobals.getPackageManager().queryIntentActivities( intent, r.resolvedType, PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS, - UserId.getCallingUserId()); + UserHandle.getCallingUserId()); // Look for the original activity in the list... final int N = resolves != null ? resolves.size() : 0; @@ -2528,7 +2532,6 @@ public final class ActivityManagerService extends ActivityManagerNative // This is so super not safe, that only the system (or okay root) // can do it. - int userId = Binder.getOrigCallingUser(); final int callingUid = Binder.getCallingUid(); if (callingUid != 0 && callingUid != Process.myUid()) { throw new SecurityException( @@ -2537,7 +2540,7 @@ public final class ActivityManagerService extends ActivityManagerNative int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, - null, null, null, null, options, userId); + null, null, null, null, options, UserHandle.getUserId(uid)); return ret; } @@ -2561,7 +2564,7 @@ public final class ActivityManagerService extends ActivityManagerNative "startActivityInPackage only available to the system"); } int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo, - options, UserId.getUserId(uid)); + options, UserHandle.getUserId(uid)); return ret; } @@ -3437,7 +3440,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); long callingId = Binder.clearCallingIdentity(); try { IPackageManager pm = AppGlobals.getPackageManager(); @@ -3511,7 +3514,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, msg); throw new SecurityException(msg); } - final int userId = UserId.getCallingUserId(); + final int userId = UserHandle.getCallingUserId(); long callingId = Binder.clearCallingIdentity(); try { IPackageManager pm = AppGlobals.getPackageManager(); @@ -3647,7 +3650,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private void forceStopPackageLocked(final String packageName, int uid) { - forceStopPackageLocked(packageName, uid, false, false, true, false, UserId.getUserId(uid)); + forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid)); Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, Uri.fromParts("package", packageName, null)); if (!mProcessesReady) { @@ -3657,7 +3660,7 @@ public final class ActivityManagerService extends ActivityManagerNative broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, false, false, - MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid)); + MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid)); } private final boolean killPackageProcessesLocked(String packageName, int uid, @@ -3764,27 +3767,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } - ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); - for (ServiceRecord service : mServiceMap.getAllServices(userId)) { - if (service.packageName.equals(name) - && (service.app == null || evenPersistent || !service.app.persistent)) { - if (!doit) { - return true; - } - didSomething = true; - Slog.i(TAG, " Force stopping service " + service); - if (service.app != null) { - service.app.removed = true; - } - service.app = null; - service.isolatedProc = null; - services.add(service); + if (mServices.forceStopLocked(name, userId, evenPersistent, doit)) { + if (!doit) { + return true; } - } - - N = services.size(); - for (i=0; i<N; i++) { - bringDownServiceLocked(services.get(i), true); + didSomething = true; } ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>(); @@ -3884,18 +3871,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Take care of any launching providers waiting for this process. checkAppInLaunchingProvidersLocked(app, true); // Take care of any services that are waiting for the process. - for (int i=0; i<mPendingServices.size(); i++) { - ServiceRecord sr = mPendingServices.get(i); - if ((app.uid == sr.appInfo.uid - && app.processName.equals(sr.processName)) - || sr.isolatedProc == app) { - Slog.w(TAG, "Forcing bringing down service: " + sr); - sr.isolatedProc = null; - mPendingServices.remove(i); - i--; - bringDownServiceLocked(sr, true); - } - } + mServices.processStartTimedOutLocked(app); EventLog.writeEvent(EventLogTags.AM_KILL, pid, app.processName, app.setAdj, "start timeout"); Process.killProcessQuiet(pid); @@ -4095,24 +4071,10 @@ public final class ActivityManagerService extends ActivityManagerNative } // Find any services that should be running in this process... - if (!badApp && mPendingServices.size() > 0) { - ServiceRecord sr = null; + if (!badApp) { try { - for (int i=0; i<mPendingServices.size(); i++) { - sr = mPendingServices.get(i); - if (app != sr.isolatedProc && (app.uid != sr.appInfo.uid - || !processName.equals(sr.processName))) { - continue; - } - - mPendingServices.remove(i); - i--; - realStartServiceLocked(sr, app); - didSomething = true; - } + didSomething |= mServices.attachApplicationLocked(app, processName); } catch (Exception e) { - Slog.w(TAG, "Exception in new application when starting service " - + sr.shortName, e); badApp = true; } } @@ -4265,12 +4227,14 @@ public final class ActivityManagerService extends ActivityManagerNative // Tell anyone interested that we are done booting! SystemProperties.set("sys.boot_completed", "1"); SystemProperties.set("dev.bootcomplete", "1"); - /* TODO: Send this to all users that are to be logged in on startup */ - broadcastIntentLocked(null, null, - new Intent(Intent.ACTION_BOOT_COMPLETED, null), - null, null, 0, null, null, - android.Manifest.permission.RECEIVE_BOOT_COMPLETED, - false, false, MY_PID, Process.SYSTEM_UID, Binder.getOrigCallingUser()); + List<UserInfo> users = getUserManager().getUsers(); + for (UserInfo user : users) { + broadcastIntentLocked(null, null, + new Intent(Intent.ACTION_BOOT_COMPLETED, null), + null, null, 0, null, null, + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, + false, false, MY_PID, Process.SYSTEM_UID, user.id); + } } } } @@ -4417,8 +4381,8 @@ public final class ActivityManagerService extends ActivityManagerNative try { if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { int uid = AppGlobals.getPackageManager() - .getPackageUid(packageName, UserId.getUserId(callingUid)); - if (!UserId.isSameApp(callingUid, uid)) { + .getPackageUid(packageName, UserHandle.getUserId(callingUid)); + if (!UserHandle.isSameApp(callingUid, uid)) { String msg = "Permission Denial: getIntentSender() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() @@ -4514,8 +4478,8 @@ public final class ActivityManagerService extends ActivityManagerNative PendingIntentRecord rec = (PendingIntentRecord)sender; try { int uid = AppGlobals.getPackageManager() - .getPackageUid(rec.key.packageName, UserId.getCallingUserId()); - if (!UserId.isSameApp(uid, Binder.getCallingUid())) { + .getPackageUid(rec.key.packageName, UserHandle.getCallingUserId()); + if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) { String msg = "Permission Denial: cancelIntentSender() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() @@ -4734,7 +4698,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (permission == null) { return PackageManager.PERMISSION_DENIED; } - return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true); + return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true); } /** @@ -4744,7 +4708,7 @@ public final class ActivityManagerService extends ActivityManagerNative int checkCallingPermission(String permission) { return checkPermission(permission, Binder.getCallingPid(), - UserId.getAppId(Binder.getCallingUid())); + UserHandle.getAppId(Binder.getCallingUid())); } /** @@ -4875,7 +4839,7 @@ public final class ActivityManagerService extends ActivityManagerNative pid = tlsIdentity.pid; } - uid = UserId.getAppId(uid); + uid = UserHandle.getAppId(uid); // Our own process gets to do everything. if (pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; @@ -4921,13 +4885,13 @@ public final class ActivityManagerService extends ActivityManagerNative String name = uri.getAuthority(); ProviderInfo pi = null; ContentProviderRecord cpr = mProviderMap.getProviderByName(name, - UserId.getUserId(callingUid)); + UserHandle.getUserId(callingUid)); if (cpr != null) { pi = cpr.info; } else { try { pi = pm.resolveContentProvider(name, - PackageManager.GET_URI_PERMISSION_PATTERNS, UserId.getUserId(callingUid)); + PackageManager.GET_URI_PERMISSION_PATTERNS, UserHandle.getUserId(callingUid)); } catch (RemoteException ex) { } } @@ -4939,7 +4903,7 @@ public final class ActivityManagerService extends ActivityManagerNative int targetUid = lastTargetUid; if (targetUid < 0 && targetPkg != null) { try { - targetUid = pm.getPackageUid(targetPkg, UserId.getUserId(callingUid)); + targetUid = pm.getPackageUid(targetPkg, UserHandle.getUserId(callingUid)); if (targetUid < 0) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg); @@ -5231,7 +5195,7 @@ public final class ActivityManagerService extends ActivityManagerNative final String authority = uri.getAuthority(); ProviderInfo pi = null; - int userId = UserId.getUserId(callingUid); + int userId = UserHandle.getUserId(callingUid); ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId); if (cpr != null) { pi = cpr.info; @@ -5567,13 +5531,28 @@ public final class ActivityManagerService extends ActivityManagerNative } public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, - int flags) { + int flags, int userId) { final int callingUid = Binder.getCallingUid(); - // If it's the system uid asking, then use the current user id. - // TODO: Make sure that there aren't any other legitimate calls from the system uid that - // require the entire list. - final int callingUserId = callingUid == Process.SYSTEM_UID - ? mCurrentUserId : UserId.getUserId(callingUid); + if (userId != UserHandle.getCallingUserId()) { + // Check if the caller is holding permissions for cross-user requests. + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Binder.getCallingPid(), callingUid, -1, true) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: " + + "Request to get recent tasks for user " + userId + + " but is calling from user " + UserHandle.getUserId(callingUid) + + "; this requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } else { + if (userId == UserHandle.USER_CURRENT) { + userId = mCurrentUserId; + } + } + } + synchronized (this) { enforceCallingPermission(android.Manifest.permission.GET_TASKS, "getRecentTasks()"); @@ -5582,7 +5561,7 @@ public final class ActivityManagerService extends ActivityManagerNative == PackageManager.PERMISSION_GRANTED; IPackageManager pm = AppGlobals.getPackageManager(); - + final int N = mRecentTasks.size(); ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<ActivityManager.RecentTaskInfo>( @@ -5590,7 +5569,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N && maxNum > 0; i++) { TaskRecord tr = mRecentTasks.get(i); // Only add calling user's recent tasks - if (tr.userId != callingUserId) continue; + if (tr.userId != userId) continue; // Return the entry if desired by the caller. We always return // the first entry, because callers always expect this to be the // foreground app. We may filter others if the caller has @@ -5618,13 +5597,13 @@ public final class ActivityManagerService extends ActivityManagerNative // Check whether this activity is currently available. try { if (rti.origActivity != null) { - if (pm.getActivityInfo(rti.origActivity, 0, callingUserId) + if (pm.getActivityInfo(rti.origActivity, 0, userId) == null) { continue; } } else if (rti.baseIntent != null) { if (pm.queryIntentActivities(rti.baseIntent, - null, 0, callingUserId) == null) { + null, 0, userId) == null) { continue; } } @@ -5689,29 +5668,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // Find any running services associated with this app. - ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); - for (ServiceRecord sr : mServiceMap.getAllServices(tr.userId)) { - if (sr.packageName.equals(component.getPackageName())) { - services.add(sr); - } - } - - // Take care of any running services associated with the app. - for (int i=0; i<services.size(); i++) { - ServiceRecord sr = services.get(i); - if (sr.startRequested) { - if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) { - Slog.i(TAG, "Stopping service " + sr.shortName + ": remove task"); - stopServiceLocked(sr); - } else { - sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, - sr.makeNextStartId(), baseIntent, null)); - if (sr.app != null && sr.app.thread != null) { - sendServiceArgsLocked(sr, false); - } - } - } - } + mServices.cleanUpRemovedTaskLocked(tr, component, baseIntent); if (killProcesses) { // Find any running processes associated with this app. @@ -6055,15 +6012,26 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid); int userId = app.userId; if (providers != null) { - final int N = providers.size(); + int N = providers.size(); for (int i=0; i<N; i++) { ProviderInfo cpi = (ProviderInfo)providers.get(i); + boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo, + cpi.name, cpi.flags); + if (singleton && UserHandle.getUserId(app.uid) != 0) { + // This is a singleton provider, but a user besides the + // default user is asking to initialize a process it runs + // in... well, no, it doesn't actually run in this process, + // it runs in the process of the default user. Get rid of it. + providers.remove(i); + N--; + continue; + } ComponentName comp = new ComponentName(cpi.packageName, cpi.name); ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId); if (cpr == null) { - cpr = new ContentProviderRecord(this, cpi, app.info, comp); + cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton); mProviderMap.putProviderByClass(comp, cpr); } if (DEBUG_MU) @@ -6221,7 +6189,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // First check if this content provider has been published... - int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid()); + int userId = UserHandle.getUserId(r != null ? r.uid : Binder.getCallingUid()); cpr = mProviderMap.getProviderByName(name, userId); boolean providerRunning = cpr != null; if (providerRunning) { @@ -6296,6 +6264,7 @@ public final class ActivityManagerService extends ActivityManagerNative Binder.restoreCallingIdentity(origId); } + boolean singleton; if (!providerRunning) { try { cpi = AppGlobals.getPackageManager(). @@ -6306,7 +6275,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (cpi == null) { return null; } - if (isSingleton(cpi.processName, cpi.applicationInfo)) { + singleton = isSingleton(cpi.processName, cpi.applicationInfo, + cpi.name, cpi.flags); + if (singleton) { userId = 0; } cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId); @@ -6341,7 +6312,7 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } ai = getAppInfoForUser(ai, userId); - cpr = new ContentProviderRecord(this, cpi, ai, comp); + cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton); } catch (RemoteException ex) { // pm is in same process, this will never happen. } @@ -6766,7 +6737,7 @@ public final class ActivityManagerService extends ActivityManagerNative BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); int uid = info.uid; if (isolated) { - int userId = UserId.getUserId(uid); + int userId = UserHandle.getUserId(uid); int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1; uid = 0; while (true) { @@ -6774,7 +6745,7 @@ public final class ActivityManagerService extends ActivityManagerNative || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) { mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID; } - uid = UserId.getUid(userId, mNextIsolatedProcessUid); + uid = UserHandle.getUid(userId, mNextIsolatedProcessUid); mNextIsolatedProcessUid++; if (mIsolatedProcesses.indexOfKey(uid) < 0) { // No process for this uid, use it. @@ -6812,7 +6783,7 @@ public final class ActivityManagerService extends ActivityManagerNative // This package really, really can not be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( - info.packageName, false, UserId.getUserId(app.uid)); + info.packageName, false, UserHandle.getUserId(app.uid)); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " @@ -7219,7 +7190,7 @@ public final class ActivityManagerService extends ActivityManagerNative lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; lp.width = WindowManager.LayoutParams.WRAP_CONTENT; lp.height = WindowManager.LayoutParams.WRAP_CONTENT; - lp.gravity = Gravity.BOTTOM | Gravity.LEFT; + lp.gravity = Gravity.BOTTOM | Gravity.START; lp.format = v.getBackground().getOpacity(); lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; @@ -8423,11 +8394,19 @@ public final class ActivityManagerService extends ActivityManagerNative // assume our apps are happy - lazy create the list List<ActivityManager.ProcessErrorStateInfo> errList = null; + final boolean allUsers = ActivityManager.checkUidPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED; + int userId = UserHandle.getUserId(Binder.getCallingUid()); + synchronized (this) { // iterate across all processes for (int i=mLruProcesses.size()-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); + if (!allUsers && app.userId != userId) { + continue; + } if ((app.thread != null) && (app.crashing || app.notResponding)) { // This one's in trouble, so we'll generate a report for it // crashes are higher priority (in case there's a crash *and* an anr) @@ -8491,6 +8470,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.persistent) { outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT; } + if (app.hasActivities) { + outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES; + } outInfo.lastTrimLevel = app.trimMemoryLevel; int adj = app.curAdj; outInfo.importance = oomAdjToImportance(adj, outInfo); @@ -8501,10 +8483,17 @@ public final class ActivityManagerService extends ActivityManagerNative enforceNotIsolatedCaller("getRunningAppProcesses"); // Lazy instantiation of list List<ActivityManager.RunningAppProcessInfo> runList = null; + final boolean allUsers = ActivityManager.checkUidPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED; + int userId = UserHandle.getUserId(Binder.getCallingUid()); synchronized (this) { // Iterate across all processes for (int i=mLruProcesses.size()-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); + if (!allUsers && app.userId != userId) { + continue; + } if ((app.thread != null) && (!app.crashing && !app.notResponding)) { // Generate process state info for running application ActivityManager.RunningAppProcessInfo currApp = @@ -8550,7 +8539,7 @@ public final class ActivityManagerService extends ActivityManagerNative IPackageManager pm = AppGlobals.getPackageManager(); for (String pkg : extList) { try { - ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserId.getCallingUserId()); + ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserHandle.getCallingUserId()); if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { retList.add(info); } @@ -8723,7 +8712,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } - if (!dumpService(fd, pw, name, newArgs, 0, dumpAll)) { + if (!mServices.dumpService(fd, pw, name, newArgs, 0, dumpAll)) { pw.println("No services match: " + name); pw.println("Use -h for help."); } @@ -8744,7 +8733,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } else if ("services".equals(cmd) || "s".equals(cmd)) { synchronized (this) { - dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null); + mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null); } } else { // Dumping a single activity? @@ -8783,7 +8772,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); + needSep = mServices.dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); if (needSep) { pw.println(" "); } @@ -9134,7 +9123,9 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep); pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity); pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq); - pw.println(" mNumServiceProcs=" + mNumServiceProcs + pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs + + " mNumHiddenProcs=" + mNumHiddenProcs + + " mNumServiceProcs=" + mNumServiceProcs + " mNewNumServiceProcs=" + mNewNumServiceProcs); } @@ -9214,120 +9205,6 @@ public final class ActivityManagerService extends ActivityManagerNative /** * There are three ways to call this: - * - no service specified: dump all the services - * - a flattened component name that matched an existing service was specified as the - * first arg: dump that one service - * - the first arg isn't the flattened component name of an existing service: - * dump all services whose component contains the first arg as a substring - */ - protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args, - int opti, boolean dumpAll) { - ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); - - if ("all".equals(name)) { - synchronized (this) { - try { - List<UserInfo> users = AppGlobals.getPackageManager().getUsers(); - for (UserInfo user : users) { - for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) { - services.add(r1); - } - } - } catch (RemoteException re) { - } - } - } else { - ComponentName componentName = name != null - ? ComponentName.unflattenFromString(name) : null; - int objectId = 0; - if (componentName == null) { - // Not a '/' separated full component name; maybe an object ID? - try { - objectId = Integer.parseInt(name, 16); - name = null; - componentName = null; - } catch (RuntimeException e) { - } - } - - synchronized (this) { - try { - List<UserInfo> users = AppGlobals.getPackageManager().getUsers(); - for (UserInfo user : users) { - for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) { - if (componentName != null) { - if (r1.name.equals(componentName)) { - services.add(r1); - } - } else if (name != null) { - if (r1.name.flattenToString().contains(name)) { - services.add(r1); - } - } else if (System.identityHashCode(r1) == objectId) { - services.add(r1); - } - } - } - } catch (RemoteException re) { - } - } - } - - if (services.size() <= 0) { - return false; - } - - boolean needSep = false; - for (int i=0; i<services.size(); i++) { - if (needSep) { - pw.println(); - } - needSep = true; - dumpService("", fd, pw, services.get(i), args, dumpAll); - } - return true; - } - - /** - * Invokes IApplicationThread.dumpService() on the thread of the specified service if - * there is a thread associated with the service. - */ - private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw, - final ServiceRecord r, String[] args, boolean dumpAll) { - String innerPrefix = prefix + " "; - synchronized (this) { - pw.print(prefix); pw.print("SERVICE "); - pw.print(r.shortName); pw.print(" "); - pw.print(Integer.toHexString(System.identityHashCode(r))); - pw.print(" pid="); - if (r.app != null) pw.println(r.app.pid); - else pw.println("(not running)"); - if (dumpAll) { - r.dump(pw, innerPrefix); - } - } - if (r.app != null && r.app.thread != null) { - pw.print(prefix); pw.println(" Client:"); - pw.flush(); - try { - TransferPipe tp = new TransferPipe(); - try { - r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args); - tp.setBufferPrefix(prefix + " "); - tp.go(fd); - } finally { - tp.kill(); - } - } catch (IOException e) { - pw.println(prefix + " Failure while dumping the service: " + e); - } catch (RemoteException e) { - pw.println(prefix + " Got a RemoteException while dumping the service"); - } - } - } - - /** - * There are three ways to call this: * - no provider specified: dump all the providers * - a flattened component name that matched an existing provider was specified as the * first arg: dump that one provider @@ -9608,199 +9485,6 @@ public final class ActivityManagerService extends ActivityManagerNative return needSep; } - /** - * Prints a list of ServiceRecords (dumpsys activity services) - */ - boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { - boolean needSep = false; - - ItemMatcher matcher = new ItemMatcher(); - matcher.build(args, opti); - - pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)"); - try { - List<UserInfo> users = AppGlobals.getPackageManager().getUsers(); - for (UserInfo user : users) { - if (mServiceMap.getAllServices(user.id).size() > 0) { - boolean printed = false; - long nowReal = SystemClock.elapsedRealtime(); - Iterator<ServiceRecord> it = mServiceMap.getAllServices( - user.id).iterator(); - needSep = false; - while (it.hasNext()) { - ServiceRecord r = it.next(); - if (!matcher.match(r, r.name)) { - continue; - } - if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { - continue; - } - if (!printed) { - pw.println(" Active services:"); - printed = true; - } - if (needSep) { - pw.println(); - } - pw.print(" * "); - pw.println(r); - if (dumpAll) { - r.dump(pw, " "); - needSep = true; - } else { - pw.print(" app="); - pw.println(r.app); - pw.print(" created="); - TimeUtils.formatDuration(r.createTime, nowReal, pw); - pw.print(" started="); - pw.print(r.startRequested); - pw.print(" connections="); - pw.println(r.connections.size()); - if (r.connections.size() > 0) { - pw.println(" Connections:"); - for (ArrayList<ConnectionRecord> clist : r.connections.values()) { - for (int i = 0; i < clist.size(); i++) { - ConnectionRecord conn = clist.get(i); - pw.print(" "); - pw.print(conn.binding.intent.intent.getIntent() - .toShortString(false, false, false, false)); - pw.print(" -> "); - ProcessRecord proc = conn.binding.client; - pw.println(proc != null ? proc.toShortString() : "null"); - } - } - } - } - if (dumpClient && r.app != null && r.app.thread != null) { - pw.println(" Client:"); - pw.flush(); - try { - TransferPipe tp = new TransferPipe(); - try { - r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), - r, args); - tp.setBufferPrefix(" "); - // Short timeout, since blocking here can - // deadlock with the application. - tp.go(fd, 2000); - } finally { - tp.kill(); - } - } catch (IOException e) { - pw.println(" Failure while dumping the service: " + e); - } catch (RemoteException e) { - pw.println(" Got a RemoteException while dumping the service"); - } - needSep = true; - } - } - needSep = printed; - } - } - } catch (RemoteException re) { - - } - - if (mPendingServices.size() > 0) { - boolean printed = false; - for (int i=0; i<mPendingServices.size(); i++) { - ServiceRecord r = mPendingServices.get(i); - if (!matcher.match(r, r.name)) { - continue; - } - if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { - continue; - } - if (!printed) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Pending services:"); - printed = true; - } - pw.print(" * Pending "); pw.println(r); - r.dump(pw, " "); - } - needSep = true; - } - - if (mRestartingServices.size() > 0) { - boolean printed = false; - for (int i=0; i<mRestartingServices.size(); i++) { - ServiceRecord r = mRestartingServices.get(i); - if (!matcher.match(r, r.name)) { - continue; - } - if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { - continue; - } - if (!printed) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Restarting services:"); - printed = true; - } - pw.print(" * Restarting "); pw.println(r); - r.dump(pw, " "); - } - needSep = true; - } - - if (mStoppingServices.size() > 0) { - boolean printed = false; - for (int i=0; i<mStoppingServices.size(); i++) { - ServiceRecord r = mStoppingServices.get(i); - if (!matcher.match(r, r.name)) { - continue; - } - if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { - continue; - } - if (!printed) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Stopping services:"); - printed = true; - } - pw.print(" * Stopping "); pw.println(r); - r.dump(pw, " "); - } - needSep = true; - } - - if (dumpAll) { - if (mServiceConnections.size() > 0) { - boolean printed = false; - Iterator<ArrayList<ConnectionRecord>> it - = mServiceConnections.values().iterator(); - while (it.hasNext()) { - ArrayList<ConnectionRecord> r = it.next(); - for (int i=0; i<r.size(); i++) { - ConnectionRecord cr = r.get(i); - if (!matcher.match(cr.binding.service, cr.binding.service.name)) { - continue; - } - if (dumpPackage != null && (cr.binding.client == null - || !dumpPackage.equals(cr.binding.client.info.packageName))) { - continue; - } - if (!printed) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Connection bindings to services:"); - printed = true; - } - pw.print(" * "); pw.println(cr); - cr.dump(pw, " "); - } - } - needSep = true; - } - } - - return needSep; - } - boolean dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage) { boolean needSep = true; @@ -10106,6 +9790,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" "); pw.print("oom: max="); pw.print(r.maxAdj); pw.print(" hidden="); pw.print(r.hiddenAdj); + pw.print(" empty="); pw.print(r.emptyAdj); pw.print(" curRaw="); pw.print(r.curRawAdj); pw.print(" setRaw="); pw.print(r.setRawAdj); pw.print(" cur="); pw.print(r.curAdj); @@ -10594,125 +10279,6 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } - private final void killServicesLocked(ProcessRecord app, - boolean allowRestart) { - // Report disconnected services. - if (false) { - // XXX we are letting the client link to the service for - // death notifications. - if (app.services.size() > 0) { - Iterator<ServiceRecord> it = app.services.iterator(); - while (it.hasNext()) { - ServiceRecord r = it.next(); - if (r.connections.size() > 0) { - Iterator<ArrayList<ConnectionRecord>> jt - = r.connections.values().iterator(); - while (jt.hasNext()) { - ArrayList<ConnectionRecord> cl = jt.next(); - for (int i=0; i<cl.size(); i++) { - ConnectionRecord c = cl.get(i); - if (c.binding.client != app) { - try { - //c.conn.connected(r.className, null); - } catch (Exception e) { - // todo: this should be asynchronous! - Slog.w(TAG, "Exception thrown disconnected servce " - + r.shortName - + " from app " + app.processName, e); - } - } - } - } - } - } - } - } - - // Clean up any connections this application has to other services. - if (app.connections.size() > 0) { - Iterator<ConnectionRecord> it = app.connections.iterator(); - while (it.hasNext()) { - ConnectionRecord r = it.next(); - removeConnectionLocked(r, app, null); - } - } - app.connections.clear(); - - if (app.services.size() != 0) { - // Any services running in the application need to be placed - // back in the pending list. - Iterator<ServiceRecord> it = app.services.iterator(); - while (it.hasNext()) { - ServiceRecord sr = it.next(); - synchronized (sr.stats.getBatteryStats()) { - sr.stats.stopLaunchedLocked(); - } - sr.app = null; - sr.isolatedProc = null; - sr.executeNesting = 0; - if (mStoppingServices.remove(sr)) { - if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); - } - - boolean hasClients = sr.bindings.size() > 0; - if (hasClients) { - Iterator<IntentBindRecord> bindings - = sr.bindings.values().iterator(); - while (bindings.hasNext()) { - IntentBindRecord b = bindings.next(); - if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b - + ": shouldUnbind=" + b.hasBound); - b.binder = null; - b.requested = b.received = b.hasBound = false; - } - } - - if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags - &ApplicationInfo.FLAG_PERSISTENT) == 0) { - Slog.w(TAG, "Service crashed " + sr.crashCount - + " times, stopping: " + sr); - EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, - sr.crashCount, sr.shortName, app.pid); - bringDownServiceLocked(sr, true); - } else if (!allowRestart) { - bringDownServiceLocked(sr, true); - } else { - boolean canceled = scheduleServiceRestartLocked(sr, true); - - // Should the service remain running? Note that in the - // extreme case of so many attempts to deliver a command - // that it failed we also will stop it here. - if (sr.startRequested && (sr.stopIfKilled || canceled)) { - if (sr.pendingStarts.size() == 0) { - sr.startRequested = false; - if (!hasClients) { - // Whoops, no reason to restart! - bringDownServiceLocked(sr, true); - } - } - } - } - } - - if (!allowRestart) { - app.services.clear(); - } - } - - // Make sure we have no more records on the stopping list. - int i = mStoppingServices.size(); - while (i > 0) { - i--; - ServiceRecord sr = mStoppingServices.get(i); - if (sr.app == app) { - mStoppingServices.remove(i); - if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); - } - } - - app.executingServices.clear(); - } - private final boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr, boolean always) { final boolean inLaunching = mLaunchingProviders.contains(cpr); @@ -10722,10 +10288,10 @@ public final class ActivityManagerService extends ActivityManagerNative cpr.launchingApp = null; cpr.notifyAll(); } - mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid)); + mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid)); String names[] = cpr.info.authority.split(";"); for (int j = 0; j < names.length; j++) { - mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid)); + mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid)); } } @@ -10810,7 +10376,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.hasShownUi = false; app.hasAboveClient = false; - killServicesLocked(app, allowRestart); + mServices.killServicesLocked(app, allowRestart); boolean restart = false; @@ -10972,806 +10538,23 @@ public final class ActivityManagerService extends ActivityManagerNative // SERVICES // ========================================================= - ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) { - ActivityManager.RunningServiceInfo info = - new ActivityManager.RunningServiceInfo(); - info.service = r.name; - if (r.app != null) { - info.pid = r.app.pid; - } - info.uid = r.appInfo.uid; - info.process = r.processName; - info.foreground = r.isForeground; - info.activeSince = r.createTime; - info.started = r.startRequested; - info.clientCount = r.connections.size(); - info.crashCount = r.crashCount; - info.lastActivityTime = r.lastActivity; - if (r.isForeground) { - info.flags |= ActivityManager.RunningServiceInfo.FLAG_FOREGROUND; - } - if (r.startRequested) { - info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED; - } - if (r.app != null && r.app.pid == MY_PID) { - info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS; - } - if (r.app != null && r.app.persistent) { - info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS; - } - - for (ArrayList<ConnectionRecord> connl : r.connections.values()) { - for (int i=0; i<connl.size(); i++) { - ConnectionRecord conn = connl.get(i); - if (conn.clientLabel != 0) { - info.clientPackage = conn.binding.client.info.packageName; - info.clientLabel = conn.clientLabel; - return info; - } - } - } - return info; - } - public List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags) { enforceNotIsolatedCaller("getServices"); synchronized (this) { - ArrayList<ActivityManager.RunningServiceInfo> res - = new ArrayList<ActivityManager.RunningServiceInfo>(); - - int userId = UserId.getUserId(Binder.getCallingUid()); - if (mServiceMap.getAllServices(userId).size() > 0) { - Iterator<ServiceRecord> it - = mServiceMap.getAllServices(userId).iterator(); - while (it.hasNext() && res.size() < maxNum) { - res.add(makeRunningServiceInfoLocked(it.next())); - } - } - - for (int i=0; i<mRestartingServices.size() && res.size() < maxNum; i++) { - ServiceRecord r = mRestartingServices.get(i); - ActivityManager.RunningServiceInfo info = - makeRunningServiceInfoLocked(r); - info.restarting = r.nextRestartTime; - res.add(info); - } - - return res; + return mServices.getRunningServiceInfoLocked(maxNum, flags); } } public PendingIntent getRunningServiceControlPanel(ComponentName name) { enforceNotIsolatedCaller("getRunningServiceControlPanel"); synchronized (this) { - int userId = UserId.getUserId(Binder.getCallingUid()); - ServiceRecord r = mServiceMap.getServiceByName(name, userId); - if (r != null) { - for (ArrayList<ConnectionRecord> conn : r.connections.values()) { - for (int i=0; i<conn.size(); i++) { - if (conn.get(i).clientIntent != null) { - return conn.get(i).clientIntent; - } - } - } - } + return mServices.getRunningServiceControlPanelLocked(name); } - return null; } - private final ServiceRecord findServiceLocked(ComponentName name, - IBinder token) { - ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser()); - return r == token ? r : null; - } - - private final class ServiceLookupResult { - final ServiceRecord record; - final String permission; - - ServiceLookupResult(ServiceRecord _record, String _permission) { - record = _record; - permission = _permission; - } - }; - - private ServiceLookupResult findServiceLocked(Intent service, - String resolvedType, int userId) { - ServiceRecord r = null; - if (service.getComponent() != null) { - r = mServiceMap.getServiceByName(service.getComponent(), userId); - } - if (r == null) { - Intent.FilterComparison filter = new Intent.FilterComparison(service); - r = mServiceMap.getServiceByIntent(filter, userId); - } - - if (r == null) { - try { - ResolveInfo rInfo = - AppGlobals.getPackageManager().resolveService( - service, resolvedType, 0, userId); - ServiceInfo sInfo = - rInfo != null ? rInfo.serviceInfo : null; - if (sInfo == null) { - return null; - } - - ComponentName name = new ComponentName( - sInfo.applicationInfo.packageName, sInfo.name); - r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser()); - } catch (RemoteException ex) { - // pm is in same process, this will never happen. - } - } - if (r != null) { - int callingPid = Binder.getCallingPid(); - int callingUid = Binder.getCallingUid(); - if (checkComponentPermission(r.permission, - callingPid, callingUid, r.appInfo.uid, r.exported) - != PackageManager.PERMISSION_GRANTED) { - if (!r.exported) { - Slog.w(TAG, "Permission Denial: Accessing service " + r.name - + " from pid=" + callingPid - + ", uid=" + callingUid - + " that is not exported from uid " + r.appInfo.uid); - return new ServiceLookupResult(null, "not exported from uid " - + r.appInfo.uid); - } - Slog.w(TAG, "Permission Denial: Accessing service " + r.name - + " from pid=" + callingPid - + ", uid=" + callingUid - + " requires " + r.permission); - return new ServiceLookupResult(null, r.permission); - } - return new ServiceLookupResult(r, null); - } - return null; - } - - private class ServiceRestarter implements Runnable { - private ServiceRecord mService; - - void setService(ServiceRecord service) { - mService = service; - } - - public void run() { - synchronized(ActivityManagerService.this) { - performServiceRestartLocked(mService); - } - } - } - - private ServiceLookupResult retrieveServiceLocked(Intent service, - String resolvedType, int callingPid, int callingUid, int userId) { - ServiceRecord r = null; - if (DEBUG_SERVICE) - Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType - + " callingUid=" + callingUid); - - if (service.getComponent() != null) { - r = mServiceMap.getServiceByName(service.getComponent(), userId); - } - if (r == null) { - Intent.FilterComparison filter = new Intent.FilterComparison(service); - r = mServiceMap.getServiceByIntent(filter, userId); - } - if (r == null) { - try { - ResolveInfo rInfo = - AppGlobals.getPackageManager().resolveService( - service, resolvedType, STOCK_PM_FLAGS, userId); - ServiceInfo sInfo = - rInfo != null ? rInfo.serviceInfo : null; - if (sInfo == null) { - Slog.w(TAG, "Unable to start service " + service + - ": not found"); - return null; - } - if (userId > 0) { - if (isSingleton(sInfo.processName, sInfo.applicationInfo)) { - userId = 0; - } - sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo, userId); - } - ComponentName name = new ComponentName( - sInfo.applicationInfo.packageName, sInfo.name); - r = mServiceMap.getServiceByName(name, userId); - if (r == null) { - Intent.FilterComparison filter = new Intent.FilterComparison( - service.cloneFilter()); - ServiceRestarter res = new ServiceRestarter(); - BatteryStatsImpl.Uid.Pkg.Serv ss = null; - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - ss = stats.getServiceStatsLocked( - sInfo.applicationInfo.uid, sInfo.packageName, - sInfo.name); - } - r = new ServiceRecord(this, ss, name, filter, sInfo, res); - res.setService(r); - mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r); - mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r); - - // Make sure this component isn't in the pending list. - int N = mPendingServices.size(); - for (int i=0; i<N; i++) { - ServiceRecord pr = mPendingServices.get(i); - if (pr.name.equals(name)) { - mPendingServices.remove(i); - i--; - N--; - } - } - } - } catch (RemoteException ex) { - // pm is in same process, this will never happen. - } - } - if (r != null) { - if (checkComponentPermission(r.permission, - callingPid, callingUid, r.appInfo.uid, r.exported) - != PackageManager.PERMISSION_GRANTED) { - if (!r.exported) { - Slog.w(TAG, "Permission Denial: Accessing service " + r.name - + " from pid=" + callingPid - + ", uid=" + callingUid - + " that is not exported from uid " + r.appInfo.uid); - return new ServiceLookupResult(null, "not exported from uid " - + r.appInfo.uid); - } - Slog.w(TAG, "Permission Denial: Accessing service " + r.name - + " from pid=" + callingPid - + ", uid=" + callingUid - + " requires " + r.permission); - return new ServiceLookupResult(null, r.permission); - } - return new ServiceLookupResult(r, null); - } - return null; - } - - private final void bumpServiceExecutingLocked(ServiceRecord r, String why) { - if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING " - + why + " of " + r + " in app " + r.app); - else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING " - + why + " of " + r.shortName); - long now = SystemClock.uptimeMillis(); - if (r.executeNesting == 0 && r.app != null) { - if (r.app.executingServices.size() == 0) { - Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); - msg.obj = r.app; - mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT); - } - r.app.executingServices.add(r); - } - r.executeNesting++; - r.executingStart = now; - } - - private final void sendServiceArgsLocked(ServiceRecord r, - boolean oomAdjusted) { - final int N = r.pendingStarts.size(); - if (N == 0) { - return; - } - - while (r.pendingStarts.size() > 0) { - try { - ServiceRecord.StartItem si = r.pendingStarts.remove(0); - if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: " - + r + " " + r.intent + " args=" + si.intent); - if (si.intent == null && N > 1) { - // If somehow we got a dummy null intent in the middle, - // then skip it. DO NOT skip a null intent when it is - // the only one in the list -- this is to support the - // onStartCommand(null) case. - continue; - } - si.deliveredTime = SystemClock.uptimeMillis(); - r.deliveredStarts.add(si); - si.deliveryCount++; - if (si.neededGrants != null) { - grantUriPermissionUncheckedFromIntentLocked(si.neededGrants, - si.getUriPermissionsLocked()); - } - bumpServiceExecutingLocked(r, "start"); - if (!oomAdjusted) { - oomAdjusted = true; - updateOomAdjLocked(r.app); - } - int flags = 0; - if (si.deliveryCount > 1) { - flags |= Service.START_FLAG_RETRY; - } - if (si.doneExecutingCount > 0) { - flags |= Service.START_FLAG_REDELIVERY; - } - r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); - } catch (RemoteException e) { - // Remote process gone... we'll let the normal cleanup take - // care of this. - if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r); - break; - } catch (Exception e) { - Slog.w(TAG, "Unexpected exception", e); - break; - } - } - } - - private final boolean requestServiceBindingLocked(ServiceRecord r, - IntentBindRecord i, boolean rebind) { - if (r.app == null || r.app.thread == null) { - // If service is not currently running, can't yet bind. - return false; - } - if ((!i.requested || rebind) && i.apps.size() > 0) { - try { - bumpServiceExecutingLocked(r, "bind"); - r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind); - if (!rebind) { - i.requested = true; - } - i.hasBound = true; - i.doRebind = false; - } catch (RemoteException e) { - if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r); - return false; - } - } - return true; - } - - private final void requestServiceBindingsLocked(ServiceRecord r) { - Iterator<IntentBindRecord> bindings = r.bindings.values().iterator(); - while (bindings.hasNext()) { - IntentBindRecord i = bindings.next(); - if (!requestServiceBindingLocked(r, i, false)) { - break; - } - } - } - - private final void realStartServiceLocked(ServiceRecord r, - ProcessRecord app) throws RemoteException { - if (app.thread == null) { - throw new RemoteException(); - } - if (DEBUG_MU) - Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid - + ", ProcessRecord.uid = " + app.uid); - r.app = app; - r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); - - app.services.add(r); - bumpServiceExecutingLocked(r, "create"); - updateLruProcessLocked(app, true, true); - - boolean created = false; - try { - mStringBuilder.setLength(0); - r.intent.getIntent().toShortString(mStringBuilder, true, false, true, false); - EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE, - System.identityHashCode(r), r.shortName, - mStringBuilder.toString(), r.app.pid); - synchronized (r.stats.getBatteryStats()) { - r.stats.startLaunchedLocked(); - } - ensurePackageDexOpt(r.serviceInfo.packageName); - app.thread.scheduleCreateService(r, r.serviceInfo, - compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo)); - r.postNotification(); - created = true; - } finally { - if (!created) { - app.services.remove(r); - scheduleServiceRestartLocked(r, false); - } - } - - requestServiceBindingsLocked(r); - - // If the service is in the started state, and there are no - // pending arguments, then fake up one so its onStartCommand() will - // be called. - if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { - r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - null, null)); - } - - sendServiceArgsLocked(r, true); - } - - private final boolean scheduleServiceRestartLocked(ServiceRecord r, - boolean allowCancel) { - boolean canceled = false; - - final long now = SystemClock.uptimeMillis(); - long minDuration = SERVICE_RESTART_DURATION; - long resetTime = SERVICE_RESET_RUN_DURATION; - - if ((r.serviceInfo.applicationInfo.flags - &ApplicationInfo.FLAG_PERSISTENT) != 0) { - minDuration /= 4; - } - - // Any delivered but not yet finished starts should be put back - // on the pending list. - final int N = r.deliveredStarts.size(); - if (N > 0) { - for (int i=N-1; i>=0; i--) { - ServiceRecord.StartItem si = r.deliveredStarts.get(i); - si.removeUriPermissionsLocked(); - if (si.intent == null) { - // We'll generate this again if needed. - } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT - && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) { - r.pendingStarts.add(0, si); - long dur = SystemClock.uptimeMillis() - si.deliveredTime; - dur *= 2; - if (minDuration < dur) minDuration = dur; - if (resetTime < dur) resetTime = dur; - } else { - Slog.w(TAG, "Canceling start item " + si.intent + " in service " - + r.name); - canceled = true; - } - } - r.deliveredStarts.clear(); - } - - r.totalRestartCount++; - if (r.restartDelay == 0) { - r.restartCount++; - r.restartDelay = minDuration; - } else { - // If it has been a "reasonably long time" since the service - // was started, then reset our restart duration back to - // the beginning, so we don't infinitely increase the duration - // on a service that just occasionally gets killed (which is - // a normal case, due to process being killed to reclaim memory). - if (now > (r.restartTime+resetTime)) { - r.restartCount = 1; - r.restartDelay = minDuration; - } else { - if ((r.serviceInfo.applicationInfo.flags - &ApplicationInfo.FLAG_PERSISTENT) != 0) { - // Services in peristent processes will restart much more - // quickly, since they are pretty important. (Think SystemUI). - r.restartDelay += minDuration/2; - } else { - r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR; - if (r.restartDelay < minDuration) { - r.restartDelay = minDuration; - } - } - } - } - - r.nextRestartTime = now + r.restartDelay; - - // Make sure that we don't end up restarting a bunch of services - // all at the same time. - boolean repeat; - do { - repeat = false; - for (int i=mRestartingServices.size()-1; i>=0; i--) { - ServiceRecord r2 = mRestartingServices.get(i); - if (r2 != r && r.nextRestartTime - >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN) - && r.nextRestartTime - < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) { - r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN; - r.restartDelay = r.nextRestartTime - now; - repeat = true; - break; - } - } - } while (repeat); - - if (!mRestartingServices.contains(r)) { - mRestartingServices.add(r); - } - - r.cancelNotification(); - - mHandler.removeCallbacks(r.restarter); - mHandler.postAtTime(r.restarter, r.nextRestartTime); - r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; - Slog.w(TAG, "Scheduling restart of crashed service " - + r.shortName + " in " + r.restartDelay + "ms"); - EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, - r.shortName, r.restartDelay); - - return canceled; - } - - final void performServiceRestartLocked(ServiceRecord r) { - if (!mRestartingServices.contains(r)) { - return; - } - bringUpServiceLocked(r, r.intent.getIntent().getFlags(), true); - } - - private final boolean unscheduleServiceRestartLocked(ServiceRecord r) { - if (r.restartDelay == 0) { - return false; - } - r.resetRestartCounter(); - mRestartingServices.remove(r); - mHandler.removeCallbacks(r.restarter); - return true; - } - - private final boolean 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; - } - - if (!whileRestarting && r.restartDelay > 0) { - // If waiting for a restart, then do nothing. - return true; - } - - if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent); - - // We are now bringing the service up, so no longer in the - // restarting state. - mRestartingServices.remove(r); - - // Service is now being launched, its package can't be stopped. - try { - AppGlobals.getPackageManager().setPackageStoppedState( - r.packageName, false, r.userId); - } catch (RemoteException e) { - } catch (IllegalArgumentException e) { - Slog.w(TAG, "Failed trying to unstop package " - + r.packageName + ": " + e); - } - - final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; - final String appName = r.processName; - ProcessRecord app; - - if (!isolated) { - app = getProcessRecordLocked(appName, r.appInfo.uid); - if (DEBUG_MU) - Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); - if (app != null && app.thread != null) { - try { - app.addPackage(r.appInfo.packageName); - realStartServiceLocked(r, app); - return true; - } catch (RemoteException e) { - Slog.w(TAG, "Exception when starting service " + r.shortName, e); - } - - // If a dead object exception was thrown -- fall through to - // restart the application. - } - } else { - // If this service runs in an isolated process, then each time - // we call startProcessLocked() we will get a new isolated - // process, starting another process if we are currently waiting - // for a previous process to come up. To deal with this, we store - // in the service any current isolated process it is running in or - // waiting to have come up. - app = r.isolatedProc; - } - - // Not running -- get it started, and enqueue this service record - // to be executed when the app comes up. - if (app == null) { - if ((app=startProcessLocked(appName, r.appInfo, true, intentFlags, - "service", r.name, false, isolated)) == null) { - Slog.w(TAG, "Unable to launch app " - + r.appInfo.packageName + "/" - + r.appInfo.uid + " for service " - + r.intent.getIntent() + ": process is bad"); - bringDownServiceLocked(r, true); - return false; - } - if (isolated) { - r.isolatedProc = app; - } - } - - if (!mPendingServices.contains(r)) { - mPendingServices.add(r); - } - - return true; - } - - private final void bringDownServiceLocked(ServiceRecord r, boolean force) { - //Slog.i(TAG, "Bring down service:"); - //r.dump(" "); - - // Does it still need to run? - if (!force && r.startRequested) { - return; - } - if (r.connections.size() > 0) { - if (!force) { - // XXX should probably keep a count of the number of auto-create - // connections directly in the service. - Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator(); - while (it.hasNext()) { - ArrayList<ConnectionRecord> cr = it.next(); - for (int i=0; i<cr.size(); i++) { - if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) { - return; - } - } - } - } - - // Report to all of the connections that the service is no longer - // available. - Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator(); - while (it.hasNext()) { - ArrayList<ConnectionRecord> c = it.next(); - for (int i=0; i<c.size(); i++) { - ConnectionRecord cr = c.get(i); - // There is still a connection to the service that is - // being brought down. Mark it as dead. - cr.serviceDead = true; - try { - cr.conn.connected(r.name, null); - } catch (Exception e) { - Slog.w(TAG, "Failure disconnecting service " + r.name + - " to connection " + c.get(i).conn.asBinder() + - " (in " + c.get(i).binding.client.processName + ")", e); - } - } - } - } - - // Tell the service that it has been unbound. - if (r.bindings.size() > 0 && r.app != null && r.app.thread != null) { - Iterator<IntentBindRecord> it = r.bindings.values().iterator(); - while (it.hasNext()) { - IntentBindRecord ibr = it.next(); - if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr - + ": hasBound=" + ibr.hasBound); - if (r.app != null && r.app.thread != null && ibr.hasBound) { - try { - bumpServiceExecutingLocked(r, "bring down unbind"); - updateOomAdjLocked(r.app); - ibr.hasBound = false; - r.app.thread.scheduleUnbindService(r, - ibr.intent.getIntent()); - } catch (Exception e) { - Slog.w(TAG, "Exception when unbinding service " - + r.shortName, e); - serviceDoneExecutingLocked(r, true); - } - } - } - } - - if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent); - EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE, - System.identityHashCode(r), r.shortName, - (r.app != null) ? r.app.pid : -1); - - mServiceMap.removeServiceByName(r.name, r.userId); - mServiceMap.removeServiceByIntent(r.intent, r.userId); - r.totalRestartCount = 0; - unscheduleServiceRestartLocked(r); - - // Also make sure it is not on the pending list. - int N = mPendingServices.size(); - for (int i=0; i<N; i++) { - if (mPendingServices.get(i) == r) { - mPendingServices.remove(i); - if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r); - i--; - N--; - } - } - - r.cancelNotification(); - r.isForeground = false; - r.foregroundId = 0; - r.foregroundNoti = null; - - // Clear start entries. - r.clearDeliveredStartsLocked(); - r.pendingStarts.clear(); - - if (r.app != null) { - synchronized (r.stats.getBatteryStats()) { - r.stats.stopLaunchedLocked(); - } - r.app.services.remove(r); - if (r.app.thread != null) { - try { - bumpServiceExecutingLocked(r, "stop"); - mStoppingServices.add(r); - updateOomAdjLocked(r.app); - r.app.thread.scheduleStopService(r); - } catch (Exception e) { - Slog.w(TAG, "Exception when stopping service " - + r.shortName, e); - serviceDoneExecutingLocked(r, true); - } - updateServiceForegroundLocked(r.app, false); - } else { - if (DEBUG_SERVICE) Slog.v( - TAG, "Removed service that has no process: " + r); - } - } else { - if (DEBUG_SERVICE) Slog.v( - TAG, "Removed service that is not running: " + r); - } - - if (r.bindings.size() > 0) { - r.bindings.clear(); - } - - if (r.restarter instanceof ServiceRestarter) { - ((ServiceRestarter)r.restarter).setService(null); - } - } - - ComponentName startServiceLocked(IApplicationThread caller, - Intent service, String resolvedType, - int callingPid, int callingUid) { - synchronized(this) { - if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service - + " type=" + resolvedType + " args=" + service.getExtras()); - - if (caller != null) { - final ProcessRecord callerApp = getRecordForAppLocked(caller); - if (callerApp == null) { - throw new SecurityException( - "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() - + ") when starting service " + service); - } - } - - ServiceLookupResult res = - retrieveServiceLocked(service, resolvedType, - callingPid, callingUid, UserId.getUserId(callingUid)); - if (res == null) { - return null; - } - if (res.record == null) { - return new ComponentName("!", res.permission != null - ? res.permission : "private to package"); - } - ServiceRecord r = res.record; - NeededUriGrants neededGrants = checkGrantUriPermissionFromIntentLocked( - callingUid, r.packageName, service, service.getFlags(), null); - if (unscheduleServiceRestartLocked(r)) { - if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r); - } - r.startRequested = true; - r.callStart = false; - r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - service, neededGrants)); - r.lastActivity = SystemClock.uptimeMillis(); - synchronized (r.stats.getBatteryStats()) { - r.stats.startRunningLocked(); - } - if (!bringUpServiceLocked(r, service.getFlags(), false)) { - return new ComponentName("!", "Service process is bad"); - } - return r.name; - } - } - public ComponentName startService(IApplicationThread caller, Intent service, - String resolvedType) { + String resolvedType, int userId) { enforceNotIsolatedCaller("startService"); // Refuse possible leaked file descriptors if (service != null && service.hasFileDescriptors() == true) { @@ -11783,9 +10566,10 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); + checkValidCaller(callingUid, userId); final long origId = Binder.clearCallingIdentity(); - ComponentName res = startServiceLocked(caller, service, - resolvedType, callingPid, callingUid); + ComponentName res = mServices.startServiceLocked(caller, service, + resolvedType, callingPid, callingUid, userId); Binder.restoreCallingIdentity(origId); return res; } @@ -11797,60 +10581,26 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_SERVICE) Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType); final long origId = Binder.clearCallingIdentity(); - ComponentName res = startServiceLocked(null, service, - resolvedType, -1, uid); + ComponentName res = mServices.startServiceLocked(null, service, + resolvedType, -1, uid, UserHandle.getUserId(uid)); Binder.restoreCallingIdentity(origId); return res; } } - private void stopServiceLocked(ServiceRecord service) { - synchronized (service.stats.getBatteryStats()) { - service.stats.stopRunningLocked(); - } - service.startRequested = false; - service.callStart = false; - bringDownServiceLocked(service, false); - } - public int stopService(IApplicationThread caller, Intent service, - String resolvedType) { + String resolvedType, int userId) { enforceNotIsolatedCaller("stopService"); // Refuse possible leaked file descriptors if (service != null && service.hasFileDescriptors() == true) { throw new IllegalArgumentException("File descriptors passed in Intent"); } - synchronized(this) { - if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service - + " type=" + resolvedType); - - final ProcessRecord callerApp = getRecordForAppLocked(caller); - if (caller != null && callerApp == null) { - throw new SecurityException( - "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() - + ") when stopping service " + service); - } + checkValidCaller(Binder.getCallingUid(), userId); - // If this service is active, make sure it is stopped. - ServiceLookupResult r = findServiceLocked(service, resolvedType, - callerApp == null ? UserId.getCallingUserId() : callerApp.userId); - if (r != null) { - if (r.record != null) { - final long origId = Binder.clearCallingIdentity(); - try { - stopServiceLocked(r.record); - } finally { - Binder.restoreCallingIdentity(origId); - } - return 1; - } - return -1; - } + synchronized(this) { + return mServices.stopServiceLocked(caller, service, resolvedType, userId); } - - return 0; } public IBinder peekService(Intent service, String resolvedType) { @@ -11859,149 +10609,51 @@ public final class ActivityManagerService extends ActivityManagerNative if (service != null && service.hasFileDescriptors() == true) { throw new IllegalArgumentException("File descriptors passed in Intent"); } - - IBinder ret = null; - synchronized(this) { - ServiceLookupResult r = findServiceLocked(service, resolvedType, - UserId.getCallingUserId()); - - if (r != null) { - // r.record is null if findServiceLocked() failed the caller permission check - if (r.record == null) { - throw new SecurityException( - "Permission Denial: Accessing service " + r.record.name - + " from pid=" + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " requires " + r.permission); - } - IntentBindRecord ib = r.record.bindings.get(r.record.intent); - if (ib != null) { - ret = ib.binder; - } - } + return mServices.peekServiceLocked(service, resolvedType); } - - return ret; } public boolean stopServiceToken(ComponentName className, IBinder token, int startId) { synchronized(this) { - if (DEBUG_SERVICE) Slog.v(TAG, "stopServiceToken: " + className - + " " + token + " startId=" + startId); - ServiceRecord r = findServiceLocked(className, token); - if (r != null) { - if (startId >= 0) { - // Asked to only stop if done with all work. Note that - // to avoid leaks, we will take this as dropping all - // start items up to and including this one. - ServiceRecord.StartItem si = r.findDeliveredStart(startId, false); - if (si != null) { - while (r.deliveredStarts.size() > 0) { - ServiceRecord.StartItem cur = r.deliveredStarts.remove(0); - cur.removeUriPermissionsLocked(); - if (cur == si) { - break; - } - } - } - - if (r.getLastStartId() != startId) { - return false; - } - - if (r.deliveredStarts.size() > 0) { - Slog.w(TAG, "stopServiceToken startId " + startId - + " is last, but have " + r.deliveredStarts.size() - + " remaining args"); - } - } - - synchronized (r.stats.getBatteryStats()) { - r.stats.stopRunningLocked(); - r.startRequested = false; - r.callStart = false; - } - final long origId = Binder.clearCallingIdentity(); - bringDownServiceLocked(r, false); - Binder.restoreCallingIdentity(origId); - return true; - } + return mServices.stopServiceTokenLocked(className, token, startId); } - return false; } public void setServiceForeground(ComponentName className, IBinder token, int id, Notification notification, boolean removeNotification) { - final long origId = Binder.clearCallingIdentity(); - try { synchronized(this) { - ServiceRecord r = findServiceLocked(className, token); - if (r != null) { - if (id != 0) { - if (notification == null) { - throw new IllegalArgumentException("null notification"); - } - if (r.foregroundId != id) { - r.cancelNotification(); - r.foregroundId = id; - } - notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; - r.foregroundNoti = notification; - r.isForeground = true; - r.postNotification(); - if (r.app != null) { - updateServiceForegroundLocked(r.app, true); - } - } else { - if (r.isForeground) { - r.isForeground = false; - if (r.app != null) { - updateLruProcessLocked(r.app, false, true); - updateServiceForegroundLocked(r.app, true); - } - } - if (removeNotification) { - r.cancelNotification(); - r.foregroundId = 0; - r.foregroundNoti = null; - } - } - } - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) { - boolean anyForeground = false; - for (ServiceRecord sr : proc.services) { - if (sr.isForeground) { - anyForeground = true; - break; - } - } - if (anyForeground != proc.foregroundServices) { - proc.foregroundServices = anyForeground; - if (oomAdj) { - updateOomAdjLocked(); - } + mServices.setServiceForegroundLocked(className, token, id, notification, + removeNotification); } } - boolean isSingleton(String componentProcessName, ApplicationInfo aInfo) { + boolean isSingleton(String componentProcessName, ApplicationInfo aInfo, + String className, int flags) { boolean result = false; - if (UserId.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) { - result = false; + if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) { + if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) { + if (ActivityManager.checkUidPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + aInfo.uid) != PackageManager.PERMISSION_GRANTED) { + ComponentName comp = new ComponentName(aInfo.packageName, className); + String msg = "Permission Denial: Component " + comp.flattenToShortString() + + " requests FLAG_SINGLE_USER, but app does not hold " + + android.Manifest.permission.INTERACT_ACROSS_USERS; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + result = true; + } } else if (componentProcessName == aInfo.packageName) { result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0; } else if ("system".equals(componentProcessName)) { result = true; } if (DEBUG_MU) { - Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo + ") = " + result); + Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo + + ", " + className + ", 0x" + Integer.toHexString(flags) + ") = " + result); } return result; } @@ -12018,236 +10670,15 @@ public final class ActivityManagerService extends ActivityManagerNative checkValidCaller(Binder.getCallingUid(), userId); synchronized(this) { - if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service - + " type=" + resolvedType + " conn=" + connection.asBinder() - + " flags=0x" + Integer.toHexString(flags)); - if (DEBUG_MU) - Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid=" - + Binder.getOrigCallingUid()); - final ProcessRecord callerApp = getRecordForAppLocked(caller); - if (callerApp == null) { - throw new SecurityException( - "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() - + ") when binding service " + service); - } - - ActivityRecord activity = null; - if (token != null) { - activity = mMainStack.isInStackLocked(token); - if (activity == null) { - Slog.w(TAG, "Binding with unknown activity: " + token); - return 0; - } - } - - int clientLabel = 0; - PendingIntent clientIntent = null; - - if (callerApp.info.uid == Process.SYSTEM_UID) { - // Hacky kind of thing -- allow system stuff to tell us - // what they are, so we can report this elsewhere for - // others to know why certain services are running. - try { - clientIntent = (PendingIntent)service.getParcelableExtra( - Intent.EXTRA_CLIENT_INTENT); - } catch (RuntimeException e) { - } - if (clientIntent != null) { - clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0); - if (clientLabel != 0) { - // There are no useful extras in the intent, trash them. - // System code calling with this stuff just needs to know - // this will happen. - service = service.cloneFilter(); - } - } - } - - ServiceLookupResult res = - retrieveServiceLocked(service, resolvedType, - Binder.getCallingPid(), Binder.getCallingUid(), userId); - if (res == null) { - return 0; - } - if (res.record == null) { - return -1; - } - if (isSingleton(res.record.processName, res.record.appInfo)) { - userId = 0; - res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(), - Binder.getCallingUid(), 0); - } - ServiceRecord s = res.record; - - final long origId = Binder.clearCallingIdentity(); - - if (unscheduleServiceRestartLocked(s)) { - if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: " - + s); - } - - AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); - ConnectionRecord c = new ConnectionRecord(b, activity, - connection, flags, clientLabel, clientIntent); - - IBinder binder = connection.asBinder(); - ArrayList<ConnectionRecord> clist = s.connections.get(binder); - if (clist == null) { - clist = new ArrayList<ConnectionRecord>(); - s.connections.put(binder, clist); - } - clist.add(c); - b.connections.add(c); - if (activity != null) { - if (activity.connections == null) { - activity.connections = new HashSet<ConnectionRecord>(); - } - activity.connections.add(c); - } - b.client.connections.add(c); - if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { - b.client.hasAboveClient = true; - } - clist = mServiceConnections.get(binder); - if (clist == null) { - clist = new ArrayList<ConnectionRecord>(); - mServiceConnections.put(binder, clist); - } - clist.add(c); - - if ((flags&Context.BIND_AUTO_CREATE) != 0) { - s.lastActivity = SystemClock.uptimeMillis(); - if (!bringUpServiceLocked(s, service.getFlags(), false)) { - return 0; - } - } - - if (s.app != null) { - // This could have made the service more important. - updateOomAdjLocked(s.app); - } - - if (DEBUG_SERVICE) Slog.v(TAG, "Bind " + s + " with " + b - + ": received=" + b.intent.received - + " apps=" + b.intent.apps.size() - + " doRebind=" + b.intent.doRebind); - - if (s.app != null && b.intent.received) { - // Service is already running, so we can immediately - // publish the connection. - try { - c.conn.connected(s.name, b.intent.binder); - } catch (Exception e) { - Slog.w(TAG, "Failure sending service " + s.shortName - + " to connection " + c.conn.asBinder() - + " (in " + c.binding.client.processName + ")", e); - } - - // If this is the first app connected back to this binding, - // and the service had previously asked to be told when - // rebound, then do so. - if (b.intent.apps.size() == 1 && b.intent.doRebind) { - requestServiceBindingLocked(s, b.intent, true); - } - } else if (!b.intent.requested) { - requestServiceBindingLocked(s, b.intent, false); - } - - Binder.restoreCallingIdentity(origId); - } - - return 1; - } - - void removeConnectionLocked( - ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) { - IBinder binder = c.conn.asBinder(); - AppBindRecord b = c.binding; - ServiceRecord s = b.service; - ArrayList<ConnectionRecord> clist = s.connections.get(binder); - if (clist != null) { - clist.remove(c); - if (clist.size() == 0) { - s.connections.remove(binder); - } - } - b.connections.remove(c); - if (c.activity != null && c.activity != skipAct) { - if (c.activity.connections != null) { - c.activity.connections.remove(c); - } - } - if (b.client != skipApp) { - b.client.connections.remove(c); - if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { - b.client.updateHasAboveClientLocked(); - } - } - clist = mServiceConnections.get(binder); - if (clist != null) { - clist.remove(c); - if (clist.size() == 0) { - mServiceConnections.remove(binder); - } - } - - if (b.connections.size() == 0) { - b.intent.apps.remove(b.client); - } - - if (!c.serviceDead) { - if (DEBUG_SERVICE) Slog.v(TAG, "Disconnecting binding " + b.intent - + ": shouldUnbind=" + b.intent.hasBound); - if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 - && b.intent.hasBound) { - try { - bumpServiceExecutingLocked(s, "unbind"); - updateOomAdjLocked(s.app); - b.intent.hasBound = false; - // Assume the client doesn't want to know about a rebind; - // we will deal with that later if it asks for one. - b.intent.doRebind = false; - s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent()); - } catch (Exception e) { - Slog.w(TAG, "Exception when unbinding service " + s.shortName, e); - serviceDoneExecutingLocked(s, true); - } - } - - if ((c.flags&Context.BIND_AUTO_CREATE) != 0) { - bringDownServiceLocked(s, false); - } + return mServices.bindServiceLocked(caller, token, service, resolvedType, + connection, flags, userId); } } public boolean unbindService(IServiceConnection connection) { synchronized (this) { - IBinder binder = connection.asBinder(); - if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder); - ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder); - if (clist == null) { - Slog.w(TAG, "Unbind failed: could not find connection for " - + connection.asBinder()); - return false; - } - - final long origId = Binder.clearCallingIdentity(); - - while (clist.size() > 0) { - ConnectionRecord r = clist.get(0); - removeConnectionLocked(r, null, null); - - if (r.binding.service.app != null) { - // This could have made the service less important. - updateOomAdjLocked(r.binding.service.app); - } - } - - Binder.restoreCallingIdentity(origId); + return mServices.unbindServiceLocked(connection); } - - return true; } public void publishService(IBinder token, Intent intent, IBinder service) { @@ -12260,53 +10691,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (!(token instanceof ServiceRecord)) { throw new IllegalArgumentException("Invalid service token"); } - ServiceRecord r = (ServiceRecord)token; - - final long origId = Binder.clearCallingIdentity(); - - if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r - + " " + intent + ": " + service); - if (r != null) { - Intent.FilterComparison filter - = new Intent.FilterComparison(intent); - IntentBindRecord b = r.bindings.get(filter); - if (b != null && !b.received) { - b.binder = service; - b.requested = true; - b.received = true; - if (r.connections.size() > 0) { - Iterator<ArrayList<ConnectionRecord>> it - = r.connections.values().iterator(); - while (it.hasNext()) { - ArrayList<ConnectionRecord> clist = it.next(); - for (int i=0; i<clist.size(); i++) { - ConnectionRecord c = clist.get(i); - if (!filter.equals(c.binding.intent.intent)) { - if (DEBUG_SERVICE) Slog.v( - TAG, "Not publishing to: " + c); - if (DEBUG_SERVICE) Slog.v( - TAG, "Bound intent: " + c.binding.intent.intent); - if (DEBUG_SERVICE) Slog.v( - TAG, "Published intent: " + intent); - continue; - } - if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c); - try { - c.conn.connected(r.name, service); - } catch (Exception e) { - Slog.w(TAG, "Failure sending service " + r.name + - " to connection " + c.conn.asBinder() + - " (in " + c.binding.client.processName + ")", e); - } - } - } - } - } - - serviceDoneExecutingLocked(r, mStoppingServices.contains(r)); - - Binder.restoreCallingIdentity(origId); - } + mServices.publishServiceLocked((ServiceRecord)token, intent, service); } } @@ -12317,38 +10702,7 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized(this) { - if (!(token instanceof ServiceRecord)) { - throw new IllegalArgumentException("Invalid service token"); - } - ServiceRecord r = (ServiceRecord)token; - - final long origId = Binder.clearCallingIdentity(); - - if (r != null) { - Intent.FilterComparison filter - = new Intent.FilterComparison(intent); - IntentBindRecord b = r.bindings.get(filter); - if (DEBUG_SERVICE) Slog.v(TAG, "unbindFinished in " + r - + " at " + b + ": apps=" - + (b != null ? b.apps.size() : 0)); - - boolean inStopping = mStoppingServices.contains(r); - if (b != null) { - if (b.apps.size() > 0 && !inStopping) { - // Applications have already bound since the last - // unbind, so just rebind right here. - requestServiceBindingLocked(r, b, true); - } else { - // Note to tell the service the next time there is - // a new client. - b.doRebind = true; - } - } - - serviceDoneExecutingLocked(r, inStopping); - - Binder.restoreCallingIdentity(origId); - } + mServices.unbindFinishedLocked((ServiceRecord)token, intent, doRebind); } } @@ -12357,137 +10711,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (!(token instanceof ServiceRecord)) { throw new IllegalArgumentException("Invalid service token"); } - ServiceRecord r = (ServiceRecord)token; - boolean inStopping = mStoppingServices.contains(token); - if (r != null) { - if (r != token) { - Slog.w(TAG, "Done executing service " + r.name - + " with incorrect token: given " + token - + ", expected " + r); - return; - } - - if (type == 1) { - // This is a call from a service start... take care of - // book-keeping. - r.callStart = true; - switch (res) { - case Service.START_STICKY_COMPATIBILITY: - case Service.START_STICKY: { - // We are done with the associated start arguments. - r.findDeliveredStart(startId, true); - // Don't stop if killed. - r.stopIfKilled = false; - break; - } - case Service.START_NOT_STICKY: { - // We are done with the associated start arguments. - r.findDeliveredStart(startId, true); - if (r.getLastStartId() == startId) { - // There is no more work, and this service - // doesn't want to hang around if killed. - r.stopIfKilled = true; - } - break; - } - case Service.START_REDELIVER_INTENT: { - // We'll keep this item until they explicitly - // call stop for it, but keep track of the fact - // that it was delivered. - ServiceRecord.StartItem si = r.findDeliveredStart(startId, false); - if (si != null) { - si.deliveryCount = 0; - si.doneExecutingCount++; - // Don't stop if killed. - r.stopIfKilled = true; - } - break; - } - case Service.START_TASK_REMOVED_COMPLETE: { - // Special processing for onTaskRemoved(). Don't - // impact normal onStartCommand() processing. - r.findDeliveredStart(startId, true); - break; - } - default: - throw new IllegalArgumentException( - "Unknown service start result: " + res); - } - if (res == Service.START_STICKY_COMPATIBILITY) { - r.callStart = false; - } - } - if (DEBUG_MU) - Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid=" - + Binder.getOrigCallingUid()); - final long origId = Binder.clearCallingIdentity(); - serviceDoneExecutingLocked(r, inStopping); - Binder.restoreCallingIdentity(origId); - } else { - Slog.w(TAG, "Done executing unknown service from pid " - + Binder.getCallingPid()); - } - } - } - - public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) { - if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r - + ": nesting=" + r.executeNesting - + ", inStopping=" + inStopping + ", app=" + r.app); - else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName); - r.executeNesting--; - if (r.executeNesting <= 0 && r.app != null) { - if (DEBUG_SERVICE) Slog.v(TAG, - "Nesting at 0 of " + r.shortName); - r.app.executingServices.remove(r); - if (r.app.executingServices.size() == 0) { - if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG, - "No more executingServices of " + r.shortName); - mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app); - } - if (inStopping) { - if (DEBUG_SERVICE) Slog.v(TAG, - "doneExecuting remove stopping " + r); - mStoppingServices.remove(r); - r.bindings.clear(); - } - updateOomAdjLocked(r.app); - } - } - - void serviceTimeout(ProcessRecord proc) { - String anrMessage = null; - - synchronized(this) { - if (proc.executingServices.size() == 0 || proc.thread == null) { - return; - } - long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT; - Iterator<ServiceRecord> it = proc.executingServices.iterator(); - ServiceRecord timeout = null; - long nextTime = 0; - while (it.hasNext()) { - ServiceRecord sr = it.next(); - if (sr.executingStart < maxTime) { - timeout = sr; - break; - } - if (sr.executingStart > nextTime) { - nextTime = sr.executingStart; - } - } - if (timeout != null && mLruProcesses.contains(proc)) { - Slog.w(TAG, "Timeout executing service: " + timeout); - anrMessage = "Executing service " + timeout.shortName; - } else { - Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); - msg.obj = proc; - mHandler.sendMessageAtTime(msg, nextTime+SERVICE_TIMEOUT); - } - } - - if (anrMessage != null) { - appNotResponding(proc, null, null, anrMessage); + mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res); } } @@ -12513,7 +10737,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Backup agent is now in use, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( - app.packageName, false, UserId.getUserId(app.uid)); + app.packageName, false, UserHandle.getUserId(app.uid)); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " @@ -12674,6 +10898,7 @@ public final class ActivityManagerService extends ActivityManagerNative public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission) { enforceNotIsolatedCaller("registerReceiver"); + int callingUid; synchronized(this) { ProcessRecord callerApp = null; if (caller != null) { @@ -12689,8 +10914,10 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException("Given caller package " + callerPackage + " is not running in process " + callerApp); } + callingUid = callerApp.info.uid; } else { callerPackage = null; + callingUid = Binder.getCallingUid(); } List allSticky = null; @@ -12735,7 +10962,8 @@ public final class ActivityManagerService extends ActivityManagerNative } mRegisteredReceivers.put(receiver.asBinder(), rl); } - BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission); + BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, + permission, callingUid); rl.add(bf); if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadast"); @@ -12754,7 +10982,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, null, -1, -1, null, receivers, null, 0, null, null, - false, true, true); + false, true, true, false); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } @@ -12846,7 +11074,34 @@ public final class ActivityManagerService extends ActivityManagerNative if ((resultTo != null) && !ordered) { Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!"); } - + + boolean onlySendToCaller = false; + + // If the caller is trying to send this broadcast to a different + // user, verify that is allowed. + if (UserHandle.getUserId(callingUid) != userId) { + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + callingPid, callingUid, -1, true) + != PackageManager.PERMISSION_GRANTED) { + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + callingPid, callingUid, -1, true) + == PackageManager.PERMISSION_GRANTED) { + onlySendToCaller = true; + } else { + String msg = "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + + " asks to send as user " + userId + + " but is calling from user " + UserHandle.getUserId(callingUid) + + "; this requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + } + // Handle special intents: if this broadcast is from the package // manager about a package being removed, we need to remove all of // its activities from the history stack. @@ -12942,7 +11197,8 @@ public final class ActivityManagerService extends ActivityManagerNative * processes) from sending protected broadcasts. */ if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID - || callingUid == Process.SHELL_UID || callingUid == 0) { + || callingUid == Process.SHELL_UID || callingUid == Process.BLUETOOTH_UID || + callingUid == 0) { // Always okay. } else if (callerApp == null || !callerApp.persistent) { try { @@ -13003,30 +11259,15 @@ public final class ActivityManagerService extends ActivityManagerNative List receivers = null; List<BroadcastFilter> registeredReceivers = null; try { - if (intent.getComponent() != null) { - // Broadcast is going to one specific receiver class... - ActivityInfo ai = AppGlobals.getPackageManager(). - getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS, userId); - if (ai != null) { - receivers = new ArrayList(); - ResolveInfo ri = new ResolveInfo(); - if (isSingleton(ai.processName, ai.applicationInfo)) { - ri.activityInfo = getActivityInfoForUser(ai, 0); - } else { - ri.activityInfo = getActivityInfoForUser(ai, userId); - } - receivers.add(ri); - } - } else { - // Need to resolve the intent to interested receivers... - if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) - == 0) { - receivers = - AppGlobals.getPackageManager().queryIntentReceivers( - intent, resolvedType, STOCK_PM_FLAGS, userId); - } - registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false, - userId); + // Need to resolve the intent to interested receivers... + if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) + == 0) { + receivers = AppGlobals.getPackageManager().queryIntentReceivers( + intent, resolvedType, STOCK_PM_FLAGS, userId); + } + if (intent.getComponent() == null) { + registeredReceivers = mReceiverResolver.queryIntent(intent, + resolvedType, false, userId); } } catch (RemoteException ex) { // pm is in same process, this will never happen. @@ -13047,7 +11288,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, registeredReceivers, resultTo, resultCode, resultData, map, - ordered, sticky, false); + ordered, sticky, false, onlySendToCaller); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); @@ -13137,7 +11378,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, receivers, resultTo, resultCode, resultData, map, ordered, - sticky, false); + sticky, false, onlySendToCaller); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing ordered broadcast " + r + ": prev had " + queue.mOrderedBroadcasts.size()); @@ -13345,7 +11586,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); final long origId = Binder.clearCallingIdentity(); // Instrumentation can kill and relaunch even persistent processes forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId); @@ -13408,7 +11649,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void finishInstrumentation(IApplicationThread target, int resultCode, Bundle results) { - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); // Refuse possible leaked file descriptors if (results != null && results.hasFileDescriptors()) { throw new IllegalArgumentException("File descriptors passed in Intent"); @@ -13721,7 +11962,7 @@ public final class ActivityManagerService extends ActivityManagerNative } else { try { ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( - destIntent.getComponent(), 0, UserId.getCallingUserId()); + destIntent.getComponent(), 0, UserHandle.getCallingUserId()); int res = mMainStack.startActivityLocked(srec.app.thread, destIntent, null, aInfo, parent.appToken, null, 0, -1, parent.launchedFromUid, 0, null, true, null); @@ -13773,14 +12014,15 @@ public final class ActivityManagerService extends ActivityManagerNative } private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, - ProcessRecord TOP_APP, boolean recursed, boolean doingAll) { + int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) { if (mAdjSeq == app.adjSeq) { // This adjustment has already been computed. If we are calling // from the top, we may have already computed our adjustment with // an earlier hidden adjustment that isn't really for us... if // so, use the new hidden adjustment. if (!recursed && app.hidden) { - app.curAdj = app.curRawAdj = app.nonStoppingAdj = hiddenAdj; + app.curAdj = app.curRawAdj = app.nonStoppingAdj = + app.hasActivities ? hiddenAdj : emptyAdj; } return app.curRawAdj; } @@ -13788,7 +12030,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.thread == null) { app.adjSeq = mAdjSeq; app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; - return (app.curAdj=ProcessList.HIDDEN_APP_MAX_ADJ); + return (app.curAdj=app.curRawAdj=ProcessList.HIDDEN_APP_MAX_ADJ); } app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; @@ -13805,6 +12047,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "fixed"; app.adjSeq = mAdjSeq; app.curRawAdj = app.nonStoppingAdj = app.maxAdj; + app.hasActivities = false; app.foregroundActivities = false; app.keeping = true; app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; @@ -13815,12 +12058,15 @@ public final class ActivityManagerService extends ActivityManagerNative app.systemNoUi = true; if (app == TOP_APP) { app.systemNoUi = false; + app.hasActivities = true; } else if (activitiesSize > 0) { for (int j = 0; j < activitiesSize; j++) { final ActivityRecord r = app.activities.get(j); if (r.visible) { app.systemNoUi = false; - break; + } + if (r.app == app) { + app.hasActivities = true; } } } @@ -13829,6 +12075,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.keeping = false; app.systemNoUi = false; + app.hasActivities = false; // Determine the importance of the process, starting with most // important to least, and assign an appropriate OOM adjustment. @@ -13844,6 +12091,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "top-activity"; foregroundActivities = true; interesting = true; + app.hasActivities = true; } else if (app.instrumentationClass != null) { // Don't want to kill running instrumentation. adj = ProcessList.FOREGROUND_APP_ADJ; @@ -13865,21 +12113,13 @@ public final class ActivityManagerService extends ActivityManagerNative adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = Process.THREAD_GROUP_DEFAULT; app.adjType = "exec-service"; - } else if (activitiesSize > 0) { - // This app is in the background with paused activities. - // We inspect activities to potentially upgrade adjustment further below. - adj = hiddenAdj; - schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; - app.hidden = true; - app.adjType = "bg-activities"; } else { - // A very not-needed process. If this is lower in the lru list, - // we will push it in to the empty bucket. + // Assume process is hidden (has activities); we will correct + // later if this is not the case. adj = hiddenAdj; schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.hidden = true; - app.empty = true; - app.adjType = "bg-empty"; + app.adjType = "bg-activities"; } boolean hasStoppingActivities = false; @@ -13896,6 +12136,7 @@ public final class ActivityManagerService extends ActivityManagerNative } schedGroup = Process.THREAD_GROUP_DEFAULT; app.hidden = false; + app.hasActivities = true; foregroundActivities = true; break; } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) { @@ -13913,9 +12154,20 @@ public final class ActivityManagerService extends ActivityManagerNative foregroundActivities = true; hasStoppingActivities = true; } + if (r.app == app) { + app.hasActivities = true; + } } } + if (adj == hiddenAdj && !app.hasActivities) { + // Whoops, this process is completely empty as far as we know + // at this point. + adj = emptyAdj; + app.empty = true; + app.adjType = "bg-empty"; + } + if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { if (app.foregroundServices) { // The user is aware of this app, so make it visible. @@ -14003,7 +12255,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "started-bg-ui-services"; } } else { - if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) { + if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) { // This service has seen some activity within // recent memory, so we will keep its process ahead // of the background processes. @@ -14049,8 +12301,16 @@ public final class ActivityManagerService extends ActivityManagerNative myHiddenAdj = ProcessList.VISIBLE_APP_ADJ; } } - clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP, true, doingAll); + int myEmptyAdj = emptyAdj; + if (myEmptyAdj > client.emptyAdj) { + if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) { + myEmptyAdj = client.emptyAdj; + } else { + myEmptyAdj = ProcessList.VISIBLE_APP_ADJ; + } + } + clientAdj = computeOomAdjLocked(client, myHiddenAdj, + myEmptyAdj, TOP_APP, true, doingAll); String adjType = null; if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { // Not doing bind OOM management, so treat @@ -14066,7 +12326,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.hidden = false; clientAdj = adj; } else { - if (now >= (s.lastActivity+MAX_SERVICE_INACTIVITY)) { + if (now >= (s.lastActivity + + ActiveServices.MAX_SERVICE_INACTIVITY)) { // This service has not seen activity within // recent memory, so allow it to drop to the // LRU list if there is no other reason to keep @@ -14188,8 +12449,16 @@ public final class ActivityManagerService extends ActivityManagerNative myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ; } } - int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP, true, doingAll); + int myEmptyAdj = emptyAdj; + if (myEmptyAdj > client.emptyAdj) { + if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) { + myEmptyAdj = client.emptyAdj; + } else { + myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ; + } + } + int clientAdj = computeOomAdjLocked(client, myHiddenAdj, + myEmptyAdj, TOP_APP, true, doingAll); if (adj > clientAdj) { if (app.hasShownUi && app != mHomeProcess && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -14602,9 +12871,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final boolean updateOomAdjLocked( - ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP, boolean doingAll) { + private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj, + int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) { app.hiddenAdj = hiddenAdj; + app.emptyAdj = emptyAdj; if (app.thread == null) { return false; @@ -14614,7 +12884,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean success = true; - computeOomAdjLocked(app, hiddenAdj, TOP_APP, false, doingAll); + computeOomAdjLocked(app, hiddenAdj, emptyAdj, TOP_APP, false, doingAll); if (app.curRawAdj != app.setRawAdj) { if (wasKeeping && !app.keeping) { @@ -14692,7 +12962,7 @@ public final class ActivityManagerService extends ActivityManagerNative return resumedActivity; } - private final boolean updateOomAdjLocked(ProcessRecord app) { + final boolean updateOomAdjLocked(ProcessRecord app) { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; int curAdj = app.curAdj; @@ -14701,7 +12971,8 @@ public final class ActivityManagerService extends ActivityManagerNative mAdjSeq++; - boolean success = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP, false); + boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.emptyAdj, + TOP_APP, false); final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ && app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ; if (nowHidden != wasHidden) { @@ -14729,34 +13000,53 @@ public final class ActivityManagerService extends ActivityManagerNative // how many slots we have for background processes; we may want // to put multiple processes in a slot of there are enough of // them. - int numSlots = ProcessList.HIDDEN_APP_MAX_ADJ - ProcessList.HIDDEN_APP_MIN_ADJ + 1; - int factor = (mLruProcesses.size()-4)/numSlots; - if (factor < 1) factor = 1; - int step = 0; + int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ + - ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2; + int emptyFactor = (mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs)/numSlots; + if (emptyFactor < 1) emptyFactor = 1; + int hiddenFactor = (mNumHiddenProcs > 0 ? mNumHiddenProcs : 1)/numSlots; + if (hiddenFactor < 1) hiddenFactor = 1; + int stepHidden = 0; + int stepEmpty = 0; + final int emptyProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit; + final int hiddenProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit; int numHidden = 0; + int numEmpty = 0; int numTrimming = 0; - + + mNumNonHiddenProcs = 0; + mNumHiddenProcs = 0; + // First update the OOM adjustment for each of the // application processes based on their current state. int i = mLruProcesses.size(); int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ; + int nextHiddenAdj = curHiddenAdj+1; + int curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ; + int nextEmptyAdj = curEmptyAdj+2; while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj); - updateOomAdjLocked(app, curHiddenAdj, TOP_APP, true); - if (curHiddenAdj < ProcessList.HIDDEN_APP_MAX_ADJ - && app.curAdj == curHiddenAdj) { - step++; - if (step >= factor) { - step = 0; - curHiddenAdj++; - } - } + updateOomAdjLocked(app, curHiddenAdj, curEmptyAdj, TOP_APP, true); if (!app.killedBackground) { - if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { + if (app.curRawAdj == curHiddenAdj && app.hasActivities) { + // This process was assigned as a hidden process... step the + // hidden level. + mNumHiddenProcs++; + if (curHiddenAdj != nextHiddenAdj) { + stepHidden++; + if (stepHidden >= hiddenFactor) { + stepHidden = 0; + curHiddenAdj = nextHiddenAdj; + nextHiddenAdj += 2; + if (nextHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) { + nextHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ; + } + } + } numHidden++; - if (numHidden > mProcessLimit) { + if (numHidden > hiddenProcessLimit) { Slog.i(TAG, "No longer want " + app.processName + " (pid " + app.pid + "): hidden #" + numHidden); EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, @@ -14764,8 +13054,37 @@ public final class ActivityManagerService extends ActivityManagerNative app.killedBackground = true; Process.killProcessQuiet(app.pid); } + } else { + if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curHiddenAdj) { + // This process was assigned as an empty process... step the + // empty level. + if (curEmptyAdj != nextEmptyAdj) { + stepEmpty++; + if (stepEmpty >= emptyFactor) { + stepEmpty = 0; + curEmptyAdj = nextEmptyAdj; + nextEmptyAdj += 2; + if (nextEmptyAdj > ProcessList.HIDDEN_APP_MAX_ADJ) { + nextEmptyAdj = ProcessList.HIDDEN_APP_MAX_ADJ; + } + } + } + } else if (app.curRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) { + mNumNonHiddenProcs++; + } + if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { + numEmpty++; + if (numEmpty > emptyProcessLimit) { + Slog.i(TAG, "No longer want " + app.processName + + " (pid " + app.pid + "): empty #" + numEmpty); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "too many background"); + app.killedBackground = true; + Process.killProcessQuiet(app.pid); + } + } } - if (!app.killedBackground && app.isolated && app.services.size() <= 0) { + if (app.isolated && app.services.size() <= 0) { // If this is an isolated process, and there are no // services running in it, then the process is no longer // needed. We agressively kill these because we can by @@ -14795,18 +13114,20 @@ public final class ActivityManagerService extends ActivityManagerNative // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. - if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/2)) { + if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/4) + && numEmpty <= (ProcessList.MAX_HIDDEN_APPS/4)) { + final int numHiddenAndEmpty = numHidden + numEmpty; final int N = mLruProcesses.size(); - factor = numTrimming/3; + int factor = numTrimming/3; int minFactor = 2; if (mHomeProcess != null) minFactor++; if (mPreviousProcess != null) minFactor++; if (factor < minFactor) factor = minFactor; - step = 0; + int step = 0; int fgTrimLevel; - if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/5)) { + if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/5)) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; - } else if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/3)) { + } else if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/3)) { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; } else { fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; @@ -15165,9 +13486,6 @@ public final class ActivityManagerService extends ActivityManagerNative // Multi-user methods - private int mCurrentUserId; - private SparseIntArray mLoggedInUsers = new SparseIntArray(5); - public boolean switchUser(int userId) { final int callingUid = Binder.getCallingUid(); if (callingUid != 0 && callingUid != Process.myUid()) { @@ -15197,8 +13515,8 @@ public final class ActivityManagerService extends ActivityManagerNative // Inform of user switch Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED); - addedIntent.putExtra(Intent.EXTRA_USERID, userId); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); return true; } @@ -15210,11 +13528,11 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.e(TAG, "Trying to get user from unauthorized app"); return null; } - return AppGlobals.getPackageManager().getUser(mCurrentUserId); + return getUserManager().getUserInfo(mCurrentUserId); } private void onUserRemoved(Intent intent) { - int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1); + int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); if (extraUserId < 1) return; // Kill all the processes for the user @@ -15224,7 +13542,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) { SparseArray<ProcessRecord> uids = uidMap.getValue(); for (int i = 0; i < uids.size(); i++) { - if (UserId.getUserId(uids.keyAt(i)) == extraUserId) { + if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) { pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i))); } } @@ -15238,28 +13556,29 @@ public final class ActivityManagerService extends ActivityManagerNative } private boolean userExists(int userId) { - try { - UserInfo user = AppGlobals.getPackageManager().getUser(userId); - return user != null; - } catch (RemoteException re) { - // Won't happen, in same process - } + UserInfo user = getUserManager().getUserInfo(userId); + return user != null; + } - return false; + UserManager getUserManager() { + if (mUserManager == null) { + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + } + return mUserManager; } private void checkValidCaller(int uid, int userId) { - if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return; + if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return; throw new SecurityException("Caller uid=" + uid + " is not privileged to communicate with user=" + userId); } private int applyUserId(int uid, int userId) { - return UserId.getUid(userId, uid); + return UserHandle.getUid(userId, uid); } - private ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) { + ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) { if (info == null) return null; ApplicationInfo newInfo = new ApplicationInfo(info); newInfo.uid = applyUserId(info.uid, userId); @@ -15270,7 +13589,7 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) { if (aInfo == null - || (userId < 1 && aInfo.applicationInfo.uid < UserId.PER_USER_RANGE)) { + || (userId < 1 && aInfo.applicationInfo.uid < UserHandle.PER_USER_RANGE)) { return aInfo; } @@ -15278,86 +13597,4 @@ public final class ActivityManagerService extends ActivityManagerNative info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId); return info; } - - static class ServiceMap { - - private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser - = new SparseArray<HashMap<ComponentName, ServiceRecord>>(); - private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>> - mServicesByIntentPerUser = new SparseArray< - HashMap<Intent.FilterComparison, ServiceRecord>>(); - - ServiceRecord getServiceByName(ComponentName name, int callingUser) { - // TODO: Deal with global services - if (DEBUG_MU) - Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser); - return getServices(callingUser).get(name); - } - - ServiceRecord getServiceByName(ComponentName name) { - return getServiceByName(name, -1); - } - - ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) { - // TODO: Deal with global services - if (DEBUG_MU) - Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser); - return getServicesByIntent(callingUser).get(filter); - } - - ServiceRecord getServiceByIntent(Intent.FilterComparison filter) { - return getServiceByIntent(filter, -1); - } - - void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) { - // TODO: Deal with global services - getServices(callingUser).put(name, value); - } - - void putServiceByIntent(Intent.FilterComparison filter, int callingUser, - ServiceRecord value) { - // TODO: Deal with global services - getServicesByIntent(callingUser).put(filter, value); - } - - void removeServiceByName(ComponentName name, int callingUser) { - // TODO: Deal with global services - ServiceRecord removed = getServices(callingUser).remove(name); - if (DEBUG_MU) - Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name - + " removed=" + removed); - } - - void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) { - // TODO: Deal with global services - ServiceRecord removed = getServicesByIntent(callingUser).remove(filter); - if (DEBUG_MU) - Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter - + " removed=" + removed); - } - - Collection<ServiceRecord> getAllServices(int callingUser) { - // TODO: Deal with global services - return getServices(callingUser).values(); - } - - private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) { - HashMap map = mServicesByNamePerUser.get(callingUser); - if (map == null) { - map = new HashMap<ComponentName, ServiceRecord>(); - mServicesByNamePerUser.put(callingUser, map); - } - return map; - } - - private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent( - int callingUser) { - HashMap map = mServicesByIntentPerUser.get(callingUser); - if (map == null) { - map = new HashMap<Intent.FilterComparison, ServiceRecord>(); - mServicesByIntentPerUser.put(callingUser, map); - } - return map; - } - } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index c40abb7..c70650d 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -37,7 +37,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.UserId; +import android.os.UserHandle; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -321,7 +321,7 @@ final class ActivityRecord { appToken = new Token(this); info = aInfo; launchedFromUid = _launchedFromUid; - userId = UserId.getUserId(aInfo.applicationInfo.uid); + userId = UserHandle.getUserId(aInfo.applicationInfo.uid); intent = _intent; shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; @@ -614,14 +614,14 @@ final class ActivityRecord { pendingOptions.getStartY()+pendingOptions.getStartHeight())); } break; - case ActivityOptions.ANIM_THUMBNAIL: - case ActivityOptions.ANIM_THUMBNAIL_DELAYED: - boolean delayed = (animationType == ActivityOptions.ANIM_THUMBNAIL_DELAYED); + case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP: + case ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN: + boolean scaleUp = (animationType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP); service.mWindowManager.overridePendingAppTransitionThumb( pendingOptions.getThumbnail(), pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getOnAnimationStartListener(), - delayed); + scaleUp); if (intent.getSourceBounds() == null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 9171e47..ccea41a 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -55,10 +55,11 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.UserId; +import android.os.UserHandle; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.view.Display; import android.view.WindowManagerPolicy; import java.io.IOException; @@ -500,7 +501,7 @@ final class ActivityStack { TaskRecord cp = null; - final int userId = UserId.getUserId(info.applicationInfo.uid); + final int userId = UserHandle.getUserId(info.applicationInfo.uid); final int N = mHistory.size(); for (int i=(N-1); i>=0; i--) { ActivityRecord r = mHistory.get(i); @@ -544,7 +545,7 @@ final class ActivityStack { if (info.targetActivity != null) { cls = new ComponentName(info.packageName, info.targetActivity); } - final int userId = UserId.getUserId(info.applicationInfo.uid); + final int userId = UserHandle.getUserId(info.applicationInfo.uid); final int N = mHistory.size(); for (int i=(N-1); i>=0; i--) { @@ -902,7 +903,7 @@ final class ActivityStack { mService.notifyAll(); } } - + public final Bitmap screenshotActivities(ActivityRecord who) { if (who.noDisplay) { return null; @@ -919,7 +920,8 @@ final class ActivityStack { } if (w > 0) { - return mService.mWindowManager.screenshotApplications(who.appToken, w, h); + return mService.mWindowManager.screenshotApplications(who.appToken, + Display.DEFAULT_DISPLAY, w, h); } return null; } @@ -2404,7 +2406,7 @@ final class ActivityStack { } if (err == ActivityManager.START_SUCCESS) { - final int userId = aInfo != null ? UserId.getUserId(aInfo.applicationInfo.uid) : 0; + 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)); } @@ -3012,7 +3014,8 @@ final class ActivityStack { // Collect information about the target of the Intent. ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags, profileFile, profileFd, userId); - if (aInfo != null && mService.isSingleton(aInfo.processName, aInfo.applicationInfo)) { + if (aInfo != null && (aInfo.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0 + && mService.isSingleton(aInfo.processName, aInfo.applicationInfo, null, 0)) { userId = 0; } aInfo = mService.getActivityInfoForUser(aInfo, userId); @@ -3900,7 +3903,7 @@ final class ActivityStack { Iterator<ConnectionRecord> it = r.connections.iterator(); while (it.hasNext()) { ConnectionRecord c = it.next(); - mService.removeConnectionLocked(c, null, r); + mService.mServices.removeConnectionLocked(c, null, r); } r.connections = null; } diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 8f797ec..ab20208 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -346,18 +346,18 @@ public final class BatteryStatsService extends IBatteryStats.Stub { mStats.noteFullWifiLockReleasedLocked(uid); } } - - public void noteScanWifiLockAcquired(int uid) { + + public void noteWifiScanStarted(int uid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteScanWifiLockAcquiredLocked(uid); + mStats.noteWifiScanStartedLocked(uid); } } - - public void noteScanWifiLockReleased(int uid) { + + public void noteWifiScanStopped(int uid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteScanWifiLockReleasedLocked(uid); + mStats.noteWifiScanStoppedLocked(uid); } } @@ -389,17 +389,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } - public void noteScanWifiLockAcquiredFromSource(WorkSource ws) { + public void noteWifiScanStartedFromSource(WorkSource ws) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteScanWifiLockAcquiredFromSourceLocked(ws); + mStats.noteWifiScanStartedFromSourceLocked(ws); } } - public void noteScanWifiLockReleasedFromSource(WorkSource ws) { + public void noteWifiScanStoppedFromSource(WorkSource ws) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteScanWifiLockReleasedFromSourceLocked(ws); + mStats.noteWifiScanStoppedFromSourceLocked(ws); } } diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java index b49bc22..4e6d0fa 100644 --- a/services/java/com/android/server/am/BroadcastFilter.java +++ b/services/java/com/android/server/am/BroadcastFilter.java @@ -27,13 +27,15 @@ class BroadcastFilter extends IntentFilter { final ReceiverList receiverList; final String packageName; final String requiredPermission; + final int owningUid; BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList, - String _packageName, String _requiredPermission) { + String _packageName, String _requiredPermission, int _owningUid) { super(_filter); receiverList = _receiverList; packageName = _packageName; requiredPermission = _requiredPermission; + owningUid = _owningUid; } public void dump(PrintWriter pw, String prefix) { diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 47b8c0a..7873dd8 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -20,12 +20,15 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import android.app.ActivityManager; import android.app.AppGlobals; import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -33,7 +36,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.UserId; +import android.os.UserHandle; import android.util.EventLog; import android.util.Slog; @@ -249,7 +252,7 @@ public class BroadcastQueue { finishReceiverLocked(br, br.resultCode, br.resultData, br.resultExtras, br.resultAbort, true); scheduleBroadcastsLocked(); - // We need to reset the state if we fails to start the receiver. + // We need to reset the state if we failed to start the receiver. br.state = BroadcastRecord.IDLE; throw new RuntimeException(e.getMessage()); } @@ -369,7 +372,17 @@ public class BroadcastQueue { private final void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered) { boolean skip = false; - if (filter.requiredPermission != null) { + if (r.onlySendToCaller) { + if (!UserHandle.isSameApp(r.callingUid, filter.owningUid)) { + Slog.w(TAG, "Permission Denial: broadcasting " + + r.intent.toString() + + " from " + r.callerPackage + " (pid=" + + r.callingPid + ", uid=" + r.callingUid + ")" + + " not allowed to go to different app " + filter.owningUid); + skip = true; + } + } + if (!skip && filter.requiredPermission != null) { int perm = mService.checkComponentPermission(filter.requiredPermission, r.callingPid, r.callingUid, -1, true); if (perm != PackageManager.PERMISSION_GRANTED) { @@ -382,7 +395,7 @@ public class BroadcastQueue { skip = true; } } - if (r.requiredPermission != null) { + if (!skip && r.requiredPermission != null) { int perm = mService.checkComponentPermission(r.requiredPermission, filter.receiverList.pid, filter.receiverList.uid, -1, true); if (perm != PackageManager.PERMISSION_GRANTED) { @@ -649,8 +662,23 @@ public class BroadcastQueue { ResolveInfo info = (ResolveInfo)nextReceiver; + ComponentName component = new ComponentName( + info.activityInfo.applicationInfo.packageName, + info.activityInfo.name); boolean skip = false; + if (r.onlySendToCaller) { + if (!UserHandle.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) { + Slog.w(TAG, "Permission Denial: broadcasting " + + r.intent.toString() + + " from " + r.callerPackage + " (pid=" + + r.callingPid + ", uid=" + r.callingUid + ")" + + " to " + component.flattenToShortString() + + " not allowed to go to different app " + + info.activityInfo.applicationInfo.uid); + skip = true; + } + } int perm = mService.checkComponentPermission(info.activityInfo.permission, r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid, info.activityInfo.exported); @@ -661,16 +689,14 @@ public class BroadcastQueue { + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " is not exported from uid " + info.activityInfo.applicationInfo.uid - + " due to receiver " + info.activityInfo.packageName - + "/" + info.activityInfo.name); + + " due to receiver " + component.flattenToShortString()); } else { Slog.w(TAG, "Permission Denial: broadcasting " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " requires " + info.activityInfo.permission - + " due to receiver " + info.activityInfo.packageName - + "/" + info.activityInfo.name); + + " due to receiver " + component.flattenToShortString()); } skip = true; } @@ -686,13 +712,33 @@ public class BroadcastQueue { if (perm != PackageManager.PERMISSION_GRANTED) { Slog.w(TAG, "Permission Denial: receiving " + r.intent + " to " - + info.activityInfo.applicationInfo.packageName + + component.flattenToShortString() + " requires " + r.requiredPermission + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")"); skip = true; } } + boolean isSingleton = false; + try { + isSingleton = mService.isSingleton(info.activityInfo.processName, + info.activityInfo.applicationInfo, + info.activityInfo.name, info.activityInfo.flags); + } catch (SecurityException e) { + Slog.w(TAG, e.getMessage()); + skip = true; + } + if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) { + if (ActivityManager.checkUidPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + info.activityInfo.applicationInfo.uid) + != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString() + + " requests FLAG_SINGLE_USER, but app does not hold " + + android.Manifest.permission.INTERACT_ACROSS_USERS); + skip = true; + } + } if (r.curApp != null && r.curApp.crashing) { // If the target process is crashing, just skip it. if (DEBUG_BROADCAST) Slog.v(TAG, @@ -715,17 +761,12 @@ public class BroadcastQueue { r.state = BroadcastRecord.APP_RECEIVE; String targetProcess = info.activityInfo.processName; - r.curComponent = new ComponentName( - info.activityInfo.applicationInfo.packageName, - info.activityInfo.name); - if (r.callingUid != Process.SYSTEM_UID) { - boolean isSingleton = mService.isSingleton(info.activityInfo.processName, - info.activityInfo.applicationInfo); - int targetUserId = isSingleton ? 0 : UserId.getUserId(r.callingUid); - info.activityInfo = mService.getActivityInfoForUser(info.activityInfo,targetUserId); + r.curComponent = component; + if (r.callingUid != Process.SYSTEM_UID && isSingleton) { + info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0); } r.curReceiver = info.activityInfo; - if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) { + if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) { Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, " + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = " + info.activityInfo.applicationInfo.uid); @@ -734,7 +775,7 @@ public class BroadcastQueue { // Broadcast is being executed, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( - r.curComponent.getPackageName(), false, UserId.getUserId(r.callingUid)); + r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid)); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index dd560fc..799b609 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -44,6 +44,7 @@ class BroadcastRecord extends Binder { final boolean ordered; // serialize the send to receivers? final boolean sticky; // originated from existing sticky data? final boolean initialSticky; // initial broadcast from register to sticky? + final boolean onlySendToCaller; // only allow receipt by sender's components? final String requiredPermission; // a permission the caller has required final List receivers; // contains BroadcastFilter and ResolveInfo IIntentReceiver resultTo; // who receives final result if non-null @@ -167,7 +168,7 @@ class BroadcastRecord extends Binder { int _callingPid, int _callingUid, String _requiredPermission, List _receivers, IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized, - boolean _sticky, boolean _initialSticky) { + boolean _sticky, boolean _initialSticky, boolean _onlySendToCaller) { queue = _queue; intent = _intent; callerApp = _callerApp; @@ -183,6 +184,7 @@ class BroadcastRecord extends Binder { ordered = _serialized; sticky = _sticky; initialSticky = _initialSticky; + onlySendToCaller = _onlySendToCaller; nextReceiver = 0; state = IDLE; } diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java index 3ba3fbb..3a6492e 100644 --- a/services/java/com/android/server/am/CompatModePackages.java +++ b/services/java/com/android/server/am/CompatModePackages.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.Map; @@ -12,7 +11,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; -import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; import android.app.ActivityManager; @@ -24,6 +22,7 @@ import android.content.res.CompatibilityInfo; import android.os.Handler; import android.os.Message; import android.os.RemoteException; +import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java index 0106114..5b3ff8d 100644 --- a/services/java/com/android/server/am/ConnectionRecord.java +++ b/services/java/com/android/server/am/ConnectionRecord.java @@ -18,6 +18,7 @@ package com.android.server.am; import android.app.IServiceConnection; import android.app.PendingIntent; +import android.content.Context; import java.io.PrintWriter; @@ -62,6 +63,33 @@ class ConnectionRecord { sb.append("ConnectionRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); + if ((flags&Context.BIND_AUTO_CREATE) != 0) { + sb.append("CR "); + } + if ((flags&Context.BIND_DEBUG_UNBIND) != 0) { + sb.append("DBG "); + } + if ((flags&Context.BIND_NOT_FOREGROUND) != 0) { + sb.append("NOTFG "); + } + if ((flags&Context.BIND_ABOVE_CLIENT) != 0) { + sb.append("ABCLT "); + } + if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { + sb.append("OOM "); + } + if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) { + sb.append("WPRI "); + } + if ((flags&Context.BIND_IMPORTANT) != 0) { + sb.append("IMP "); + } + if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { + sb.append("ACT "); + } + if ((flags&Context.BIND_NOT_VISIBLE) != 0) { + sb.append("NOTVIS "); + } if (serviceDead) { sb.append("DEAD "); } diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java index fb21b06..c80d63a 100644 --- a/services/java/com/android/server/am/ContentProviderRecord.java +++ b/services/java/com/android/server/am/ContentProviderRecord.java @@ -38,6 +38,7 @@ class ContentProviderRecord { final int uid; final ApplicationInfo appInfo; final ComponentName name; + final boolean singleton; public IContentProvider provider; public boolean noReleaseNeeded; // All attached clients @@ -54,12 +55,13 @@ class ContentProviderRecord { String shortStringName; public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info, - ApplicationInfo ai, ComponentName _name) { + ApplicationInfo ai, ComponentName _name, boolean _singleton) { service = _service; info = _info; uid = ai.uid; appInfo = ai; name = _name; + singleton = _singleton; noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID; } @@ -69,6 +71,7 @@ class ContentProviderRecord { uid = cpr.uid; appInfo = cpr.appInfo; name = cpr.name; + singleton = cpr.singleton; noReleaseNeeded = cpr.noReleaseNeeded; } @@ -150,6 +153,9 @@ class ContentProviderRecord { pw.print(prefix); pw.print("uid="); pw.print(uid); pw.print(" provider="); pw.println(provider); } + if (singleton) { + pw.print(prefix); pw.print("singleton="); pw.println(singleton); + } pw.print(prefix); pw.print("authority="); pw.println(info.authority); if (full) { if (info.isSyncable || info.multiprocess || info.initOrder != 0) { diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java index c94f714..0a92964 100644 --- a/services/java/com/android/server/am/IntentBindRecord.java +++ b/services/java/com/android/server/am/IntentBindRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.content.Context; import android.content.Intent; import android.os.IBinder; @@ -78,6 +79,20 @@ class IntentBindRecord { intent = _intent; } + int collectFlags() { + int flags = 0; + if (apps.size() > 0) { + for (AppBindRecord app : apps.values()) { + if (app.connections.size() > 0) { + for (ConnectionRecord conn : app.connections) { + flags |= conn.flags; + } + } + } + } + return flags; + } + public String toString() { if (stringName != null) { return stringName; @@ -86,6 +101,9 @@ class IntentBindRecord { sb.append("IntentBindRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); + if ((collectFlags()&Context.BIND_AUTO_CREATE) != 0) { + sb.append("CR "); + } sb.append(service.shortName); sb.append(':'); if (intent != null) { diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index ad15da1..d3b8510 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -25,7 +25,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserId; +import android.os.UserHandle; import android.util.Slog; import java.io.PrintWriter; @@ -259,7 +259,7 @@ class PendingIntentRecord extends IIntentSender.Stub { owner.broadcastIntentInPackage(key.packageName, uid, finalIntent, resolvedType, finishedReceiver, code, null, null, - requiredPermission, (finishedReceiver != null), false, UserId + requiredPermission, (finishedReceiver != null), false, UserHandle .getUserId(uid)); sendFinish = false; } catch (RuntimeException e) { diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index af7b314..afc060e 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -24,6 +24,7 @@ import com.android.server.wm.WindowManagerService; import android.graphics.Point; import android.util.Slog; +import android.view.Display; /** * Activity manager code dealing with processes. @@ -146,7 +147,7 @@ class ProcessList { void applyDisplaySize(WindowManagerService wm) { if (!mHaveDisplaySize) { Point p = new Point(); - wm.getInitialDisplaySize(p); + wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, p); if (p.x != 0 && p.y != 0) { updateOomLevels(p.x, p.y, true); mHaveDisplaySize = true; diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index cba9480..d372422 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -30,7 +30,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Process; import android.os.SystemClock; -import android.os.UserId; +import android.os.UserHandle; import android.util.PrintWriterPrinter; import android.util.TimeUtils; @@ -61,6 +61,7 @@ class ProcessRecord { long lruWeight; // Weight for ordering in LRU list int maxAdj; // Maximum OOM adjustment for this process int hiddenAdj; // If hidden, this is the adjustment to use + int emptyAdj; // If empty, this is the adjustment to use int curRawAdj; // Current OOM unlimited adjustment for this process int setRawAdj; // Last set OOM unlimited adjustment for this process int nonStoppingAdj; // Adjustment not counting any stopping activities @@ -73,6 +74,7 @@ class ProcessRecord { boolean serviceb; // Process currently is on the service B list boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? + boolean hasActivities; // Are there any activities running in this process? boolean foregroundServices; // Running any services that are foreground? boolean foregroundActivities; // Running any activities that are foreground? boolean systemNoUi; // This is a system process, but not currently showing UI. @@ -199,6 +201,7 @@ class ProcessRecord { pw.print(" empty="); pw.println(empty); pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); pw.print(" hidden="); pw.print(hiddenAdj); + pw.print(" empty="); pw.print(emptyAdj); pw.print(" curRaw="); pw.print(curRawAdj); pw.print(" setRaw="); pw.print(setRawAdj); pw.print(" nonStopping="); pw.print(nonStoppingAdj); @@ -215,7 +218,9 @@ class ProcessRecord { pw.print(" foregroundServices="); pw.print(foregroundServices); pw.print(" forcingToForeground="); pw.println(forcingToForeground); pw.print(prefix); pw.print("persistent="); pw.print(persistent); - pw.print(" removed="); pw.println(removed); + pw.print(" removed="); pw.print(removed); + pw.print(" hasActivities="); pw.print(hasActivities); + pw.print(" foregroundActivities="); pw.println(foregroundActivities); pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq); pw.print(" lruSeq="); pw.println(lruSeq); if (!keeping) { @@ -308,12 +313,12 @@ class ProcessRecord { info = _info; isolated = _info.uid != _uid; uid = _uid; - userId = UserId.getUserId(_uid); + userId = UserHandle.getUserId(_uid); processName = _processName; pkgList.add(_info.packageName); thread = _thread; maxAdj = ProcessList.HIDDEN_APP_MAX_ADJ; - hiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ; + hiddenAdj = emptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ; curRawAdj = setRawAdj = -100; curAdj = setAdj = -100; persistent = false; @@ -391,7 +396,7 @@ class ProcessRecord { sb.append(info.uid%Process.FIRST_APPLICATION_UID); if (uid != info.uid) { sb.append('i'); - sb.append(UserId.getAppId(uid) - Process.FIRST_ISOLATED_UID); + sb.append(UserHandle.getAppId(uid) - Process.FIRST_ISOLATED_UID); } } } diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java index e4608a2..15fbb98 100644 --- a/services/java/com/android/server/am/ProviderMap.java +++ b/services/java/com/android/server/am/ProviderMap.java @@ -20,7 +20,7 @@ import android.content.ComponentName; import android.os.Binder; import android.os.Process; import android.os.RemoteException; -import android.os.UserId; +import android.os.UserHandle; import android.util.Slog; import android.util.SparseArray; @@ -44,9 +44,9 @@ public class ProviderMap { private static final boolean DBG = false; - private final HashMap<String, ContentProviderRecord> mGlobalByName + private final HashMap<String, ContentProviderRecord> mSingletonByName = new HashMap<String, ContentProviderRecord>(); - private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass + private final HashMap<ComponentName, ContentProviderRecord> mSingletonByClass = new HashMap<ComponentName, ContentProviderRecord>(); private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser @@ -63,7 +63,7 @@ public class ProviderMap { Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()); } // Try to find it in the global list - ContentProviderRecord record = mGlobalByName.get(name); + ContentProviderRecord record = mSingletonByName.get(name); if (record != null) { return record; } @@ -81,7 +81,7 @@ public class ProviderMap { Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid()); } // Try to find it in the global list - ContentProviderRecord record = mGlobalByClass.get(name); + ContentProviderRecord record = mSingletonByClass.get(name); if (record != null) { return record; } @@ -95,10 +95,10 @@ public class ProviderMap { Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid() + ", record uid = " + record.appInfo.uid); } - if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) { - mGlobalByName.put(name, record); + if (record.singleton) { + mSingletonByName.put(name, record); } else { - final int userId = UserId.getUserId(record.appInfo.uid); + final int userId = UserHandle.getUserId(record.appInfo.uid); getProvidersByName(userId).put(name, record); } } @@ -108,19 +108,19 @@ public class ProviderMap { Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid() + ", record uid = " + record.appInfo.uid); } - if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) { - mGlobalByClass.put(name, record); + if (record.singleton) { + mSingletonByClass.put(name, record); } else { - final int userId = UserId.getUserId(record.appInfo.uid); + final int userId = UserHandle.getUserId(record.appInfo.uid); getProvidersByClass(userId).put(name, record); } } void removeProviderByName(String name, int optionalUserId) { - if (mGlobalByName.containsKey(name)) { + if (mSingletonByName.containsKey(name)) { if (DBG) Slog.i(TAG, "Removing from globalByName name=" + name); - mGlobalByName.remove(name); + mSingletonByName.remove(name); } else { // TODO: Verify this works, i.e., the caller happens to be from the correct user if (DBG) @@ -137,10 +137,10 @@ public class ProviderMap { } void removeProviderByClass(ComponentName name, int optionalUserId) { - if (mGlobalByClass.containsKey(name)) { + if (mSingletonByClass.containsKey(name)) { if (DBG) Slog.i(TAG, "Removing from globalByClass name=" + name); - mGlobalByClass.remove(name); + mSingletonByClass.remove(name); } else { if (DBG) Slog.i(TAG, @@ -207,37 +207,29 @@ public class ProviderMap { } void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) { - boolean needSep = false; - if (mGlobalByClass.size() > 0) { - if (needSep) - pw.println(" "); - pw.println(" Published content providers (by class):"); - dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass); + if (mSingletonByClass.size() > 0) { + pw.println(" Published single-user content providers (by class):"); + dumpProvidersByClassLocked(pw, dumpAll, mSingletonByClass); } - if (mProvidersByClassPerUser.size() > 1) { + pw.println(""); + for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { + HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i); pw.println(""); - for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { - HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i); - pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":"); - dumpProvidersByClassLocked(pw, dumpAll, map); - pw.println(" "); - } - } else if (mProvidersByClassPerUser.size() == 1) { - HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0); + pw.println(" Published user " + mProvidersByClassPerUser.keyAt(i) + + " content providers (by class):"); dumpProvidersByClassLocked(pw, dumpAll, map); } - needSep = true; if (dumpAll) { - pw.println(" "); - pw.println(" Authority to provider mappings:"); - dumpProvidersByNameLocked(pw, mGlobalByName); + pw.println(""); + pw.println(" Single-user authority to provider mappings:"); + dumpProvidersByNameLocked(pw, mSingletonByName); for (int i = 0; i < mProvidersByNamePerUser.size(); i++) { - if (i > 0) { - pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + ":"); - } + pw.println(""); + pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + + " authority to provider mappings:"); dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i)); } } @@ -338,6 +330,4 @@ public class ProviderMap { } } } - - } diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 828eef7..5d60b9c 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -23,6 +23,7 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationManager; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -31,7 +32,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; -import android.os.UserId; +import android.os.UserHandle; import android.util.Slog; import android.util.TimeUtils; @@ -260,6 +261,9 @@ class ServiceRecord extends Binder { IntentBindRecord b = it.next(); pw.print(prefix); pw.print("* IntentBindRecord{"); pw.print(Integer.toHexString(System.identityHashCode(b))); + if ((b.collectFlags()&Context.BIND_AUTO_CREATE) != 0) { + pw.append(" CREATE"); + } pw.println("}:"); b.dumpInService(pw, prefix + " "); } @@ -296,7 +300,7 @@ class ServiceRecord extends Binder { this.restarter = restarter; createTime = SystemClock.elapsedRealtime(); lastActivity = SystemClock.uptimeMillis(); - userId = UserId.getUserId(appInfo.uid); + userId = UserHandle.getUserId(appInfo.uid); } public AppBindRecord retrieveAppBindingLocked(Intent intent, diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index 3a767c2..1bae9ca 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -19,7 +19,7 @@ package com.android.server.am; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.os.UserId; +import android.os.UserHandle; import android.util.Slog; import java.io.PrintWriter; @@ -101,7 +101,7 @@ class TaskRecord extends ThumbnailHolder { } if (info.applicationInfo != null) { - userId = UserId.getUserId(info.applicationInfo.uid); + userId = UserHandle.getUserId(info.applicationInfo.uid); } } diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index ba65f39..7059674 100644 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -27,12 +27,12 @@ import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; +import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; import com.android.internal.app.IUsageStats; import com.android.internal.content.PackageMonitor; -import com.android.internal.os.AtomicFile; import com.android.internal.os.PkgUsageStats; import com.android.internal.util.FastXmlSerializer; diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 682ecf8..e4f1f7a 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -319,6 +319,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { public void limitReached(String limitName, String iface) {} + public void interfaceClassDataActivityChanged(String label, boolean active) {} + public int tether(String iface) { if (DBG) Log.d(TAG, "Tethering " + iface); TetherInterfaceSM sm = null; @@ -501,8 +503,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mUsbTetherRequested = false; } } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION"); - mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED); + NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra( + ConnectivityManager.EXTRA_NETWORK_INFO); + if (networkInfo != null && + networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) { + if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION"); + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED); + } } } } @@ -1135,7 +1142,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private State mStopTetheringErrorState; private State mSetDnsForwardersErrorState; - private ArrayList mNotifyList; + private ArrayList<TetherInterfaceSM> mNotifyList; private int mCurrentConnectionSequence; private int mMobileApnReserved = ConnectivityManager.TYPE_NONE; @@ -1165,7 +1172,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); addState(mSetDnsForwardersErrorState); - mNotifyList = new ArrayList(); + mNotifyList = new ArrayList<TetherInterfaceSM>(); setInitialState(mInitialState); } @@ -1362,8 +1369,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { protected void notifyTetheredOfNewUpstreamIface(String ifaceName) { if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName); mUpstreamIfaceName = ifaceName; - for (Object o : mNotifyList) { - TetherInterfaceSM sm = (TetherInterfaceSM)o; + for (TetherInterfaceSM sm : mNotifyList) { sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED, ifaceName); } @@ -1381,13 +1387,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub { switch (message.what) { case CMD_TETHER_MODE_REQUESTED: TetherInterfaceSM who = (TetherInterfaceSM)message.obj; - if (VDBG) Log.d(TAG, "Tether Mode requested by " + who.toString()); + if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); mNotifyList.add(who); transitionTo(mTetherModeAliveState); break; case CMD_TETHER_MODE_UNREQUESTED: who = (TetherInterfaceSM)message.obj; - if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who.toString()); + if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); int index = mNotifyList.indexOf(who); if (index != -1) { mNotifyList.remove(who); @@ -1424,18 +1430,29 @@ public class Tethering extends INetworkManagementEventObserver.Stub { switch (message.what) { case CMD_TETHER_MODE_REQUESTED: TetherInterfaceSM who = (TetherInterfaceSM)message.obj; + if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); mNotifyList.add(who); who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED, mUpstreamIfaceName); break; case CMD_TETHER_MODE_UNREQUESTED: who = (TetherInterfaceSM)message.obj; + if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); int index = mNotifyList.indexOf(who); if (index != -1) { + if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who); mNotifyList.remove(index); if (mNotifyList.isEmpty()) { turnOffMasterTetherSettings(); // transitions appropriately + } else { + if (DBG) { + Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() + + " live requests:"); + for (Object o : mNotifyList) Log.d(TAG, " " + o); + } } + } else { + Log.e(TAG, "TetherModeAliveState UNREQUESTED has unknown who: " + who); } break; case CMD_UPSTREAM_CHANGED: @@ -1562,7 +1579,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { pw.println(); pw.println("Tether state:"); for (Object o : mIfaces.values()) { - pw.println(" "+o.toString()); + pw.println(" " + o); } } pw.println(); diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index 4b82037..b3cbb84 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -16,8 +16,11 @@ package com.android.server.connectivity; +import static android.Manifest.permission.BIND_VPN_SERVICE; + import android.app.Notification; import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,55 +28,116 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.net.BaseNetworkStateTracker; +import android.net.ConnectivityManager; import android.net.INetworkManagementEventObserver; +import android.net.LinkProperties; import android.net.LocalSocket; import android.net.LocalSocketAddress; +import android.net.NetworkInfo; +import android.net.RouteInfo; +import android.net.NetworkInfo.DetailedState; import android.os.Binder; import android.os.FileUtils; import android.os.IBinder; +import android.os.INetworkManagementService; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteException; import android.os.SystemClock; -import android.os.SystemProperties; +import android.os.SystemService; +import android.security.Credentials; +import android.security.KeyStore; import android.util.Log; +import android.widget.Toast; import com.android.internal.R; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; +import com.android.internal.util.Preconditions; import com.android.server.ConnectivityService.VpnCallback; +import com.android.server.net.BaseNetworkObserver; import java.io.File; import java.io.InputStream; import java.io.OutputStream; +import java.net.Inet4Address; +import java.net.InetAddress; import java.nio.charset.Charsets; import java.util.Arrays; +import libcore.io.IoUtils; + /** * @hide */ -public class Vpn extends INetworkManagementEventObserver.Stub { - - private final static String TAG = "Vpn"; - - private final static String BIND_VPN_SERVICE = - android.Manifest.permission.BIND_VPN_SERVICE; +public class Vpn extends BaseNetworkStateTracker { + private static final String TAG = "Vpn"; + private static final boolean LOGD = true; + + // TODO: create separate trackers for each unique VPN to support + // automated reconnection - private final Context mContext; private final VpnCallback mCallback; private String mPackage = VpnConfig.LEGACY_VPN; private String mInterface; private Connection mConnection; private LegacyVpnRunner mLegacyVpnRunner; + private PendingIntent mStatusIntent; + private boolean mEnableNotif = true; - public Vpn(Context context, VpnCallback callback) { + public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) { + // TODO: create dedicated TYPE_VPN network type + super(ConnectivityManager.TYPE_DUMMY); mContext = context; mCallback = callback; + + try { + netService.registerObserver(mObserver); + } catch (RemoteException e) { + Log.wtf(TAG, "Problem registering observer", e); + } + } + + public void setEnableNotifications(boolean enableNotif) { + mEnableNotif = enableNotif; + } + + @Override + protected void startMonitoringInternal() { + // Ignored; events are sent through callbacks for now + } + + @Override + public boolean teardown() { + // TODO: finish migration to unique tracker for each VPN + throw new UnsupportedOperationException(); + } + + @Override + public boolean reconnect() { + // TODO: finish migration to unique tracker for each VPN + throw new UnsupportedOperationException(); + } + + @Override + public String getTcpBufferSizesPropName() { + return PROP_TCP_BUFFER_UNKNOWN; + } + + /** + * Update current state, dispaching event to listeners. + */ + private void updateState(DetailedState detailedState, String reason) { + if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason); + mNetworkInfo.setDetailedState(detailedState, reason, null); + mCallback.onStateChanged(new NetworkInfo(mNetworkInfo)); } /** @@ -112,10 +176,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // Reset the interface and hide the notification. if (mInterface != null) { jniReset(mInterface); - long identity = Binder.clearCallingIdentity(); - mCallback.restore(); - hideNotification(); - Binder.restoreCallingIdentity(identity); + final long token = Binder.clearCallingIdentity(); + try { + mCallback.restore(); + hideNotification(); + } finally { + Binder.restoreCallingIdentity(token); + } mInterface = null; } @@ -136,6 +203,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { Log.i(TAG, "Switched from " + mPackage + " to " + newPackage); mPackage = newPackage; + updateState(DetailedState.IDLE, "prepare"); return true; } @@ -144,7 +212,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { * interface. The socket is NOT closed by this method. * * @param socket The socket to be bound. - * @param name The name of the interface. + * @param interfaze The name of the interface. */ public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception { PackageManager pm = mContext.getPackageManager(); @@ -208,6 +276,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // Configure the interface. Abort if any of these steps fails. ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu)); try { + updateState(DetailedState.CONNECTING, "establish"); String interfaze = jniGetName(tun.getFd()); if (jniSetAddresses(interfaze, config.addresses) < 1) { throw new IllegalArgumentException("At least one address must be specified"); @@ -228,11 +297,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mConnection = connection; mInterface = interfaze; } catch (RuntimeException e) { - try { - tun.close(); - } catch (Exception ex) { - // ignore - } + updateState(DetailedState.FAILED, "establish"); + IoUtils.closeQuietly(tun); throw e; } Log.i(TAG, "Established by " + config.user + " on " + mInterface); @@ -242,54 +308,61 @@ public class Vpn extends INetworkManagementEventObserver.Stub { config.interfaze = mInterface; // Override DNS servers and show the notification. - long identity = Binder.clearCallingIdentity(); - mCallback.override(config.dnsServers, config.searchDomains); - showNotification(config, label, bitmap); - Binder.restoreCallingIdentity(identity); + final long token = Binder.clearCallingIdentity(); + try { + mCallback.override(config.dnsServers, config.searchDomains); + showNotification(config, label, bitmap); + } finally { + Binder.restoreCallingIdentity(token); + } + // TODO: ensure that contract class eventually marks as connected + updateState(DetailedState.AUTHENTICATING, "establish"); return tun; } - // INetworkManagementEventObserver.Stub - @Override - public void interfaceAdded(String interfaze) { - } - - // INetworkManagementEventObserver.Stub - @Override - public synchronized void interfaceStatusChanged(String interfaze, boolean up) { - if (!up && mLegacyVpnRunner != null) { - mLegacyVpnRunner.check(interfaze); + @Deprecated + public synchronized void interfaceStatusChanged(String iface, boolean up) { + try { + mObserver.interfaceStatusChanged(iface, up); + } catch (RemoteException e) { + // ignored; target is local } } - // INetworkManagementEventObserver.Stub - @Override - public void interfaceLinkStateChanged(String interfaze, boolean up) { - } - - // INetworkManagementEventObserver.Stub - @Override - public synchronized void interfaceRemoved(String interfaze) { - if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { - long identity = Binder.clearCallingIdentity(); - mCallback.restore(); - hideNotification(); - Binder.restoreCallingIdentity(identity); - mInterface = null; - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; - } else if (mLegacyVpnRunner != null) { - mLegacyVpnRunner.exit(); - mLegacyVpnRunner = null; + private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() { + @Override + public void interfaceStatusChanged(String interfaze, boolean up) { + synchronized (Vpn.this) { + if (!up && mLegacyVpnRunner != null) { + mLegacyVpnRunner.check(interfaze); + } } } - } - // INetworkManagementEventObserver.Stub - @Override - public void limitReached(String limit, String interfaze) { - } + @Override + public void interfaceRemoved(String interfaze) { + synchronized (Vpn.this) { + if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { + final long token = Binder.clearCallingIdentity(); + try { + mCallback.restore(); + hideNotification(); + } finally { + Binder.restoreCallingIdentity(token); + } + mInterface = null; + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + updateState(DetailedState.DISCONNECTED, "interfaceRemoved"); + } else if (mLegacyVpnRunner != null) { + mLegacyVpnRunner.exit(); + mLegacyVpnRunner = null; + } + } + } + } + }; private void enforceControlPermission() { // System user is allowed to control VPN. @@ -326,6 +399,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } private void showNotification(VpnConfig config, String label, Bitmap icon) { + if (!mEnableNotif) return; + mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config); + NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); @@ -341,15 +417,18 @@ public class Vpn extends INetworkManagementEventObserver.Stub { .setLargeIcon(icon) .setContentTitle(title) .setContentText(text) - .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config)) + .setContentIntent(mStatusIntent) .setDefaults(0) .setOngoing(true) - .getNotification(); + .build(); nm.notify(R.drawable.vpn_connected, notification); } } private void hideNotification() { + if (!mEnableNotif) return; + mStatusIntent = null; + NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); @@ -366,31 +445,172 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private native int jniCheck(String interfaze); private native void jniProtect(int socket, String interfaze); + private static String findLegacyVpnGateway(LinkProperties prop) { + for (RouteInfo route : prop.getRoutes()) { + // Currently legacy VPN only works on IPv4. + if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) { + return route.getGateway().getHostAddress(); + } + } + + throw new IllegalStateException("Unable to find suitable gateway"); + } + /** - * Start legacy VPN. This method stops the daemons and restart them - * if arguments are not null. Heavy things are offloaded to another - * thread, so callers will not be blocked for a long time. - * - * @param config The parameters to configure the network. - * @param raoocn The arguments to be passed to racoon. - * @param mtpd The arguments to be passed to mtpd. + * Start legacy VPN, controlling native daemons as needed. Creates a + * secondary thread to perform connection work, returning quickly. */ - public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { + public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) { + if (keyStore.state() != KeyStore.State.UNLOCKED) { + throw new IllegalStateException("KeyStore isn't unlocked"); + } + + final String iface = egress.getInterfaceName(); + final String gateway = findLegacyVpnGateway(egress); + + // Load certificates. + String privateKey = ""; + String userCert = ""; + String caCert = ""; + String serverCert = ""; + if (!profile.ipsecUserCert.isEmpty()) { + privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert; + byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert); + userCert = (value == null) ? null : new String(value, Charsets.UTF_8); + } + if (!profile.ipsecCaCert.isEmpty()) { + byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert); + caCert = (value == null) ? null : new String(value, Charsets.UTF_8); + } + if (!profile.ipsecServerCert.isEmpty()) { + byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert); + serverCert = (value == null) ? null : new String(value, Charsets.UTF_8); + } + if (privateKey == null || userCert == null || caCert == null || serverCert == null) { + throw new IllegalStateException("Cannot load credentials"); + } + + // Prepare arguments for racoon. + String[] racoon = null; + switch (profile.type) { + case VpnProfile.TYPE_L2TP_IPSEC_PSK: + racoon = new String[] { + iface, profile.server, "udppsk", profile.ipsecIdentifier, + profile.ipsecSecret, "1701", + }; + break; + case VpnProfile.TYPE_L2TP_IPSEC_RSA: + racoon = new String[] { + iface, profile.server, "udprsa", privateKey, userCert, + caCert, serverCert, "1701", + }; + break; + case VpnProfile.TYPE_IPSEC_XAUTH_PSK: + racoon = new String[] { + iface, profile.server, "xauthpsk", profile.ipsecIdentifier, + profile.ipsecSecret, profile.username, profile.password, "", gateway, + }; + break; + case VpnProfile.TYPE_IPSEC_XAUTH_RSA: + racoon = new String[] { + iface, profile.server, "xauthrsa", privateKey, userCert, + caCert, serverCert, profile.username, profile.password, "", gateway, + }; + break; + case VpnProfile.TYPE_IPSEC_HYBRID_RSA: + racoon = new String[] { + iface, profile.server, "hybridrsa", + caCert, serverCert, profile.username, profile.password, "", gateway, + }; + break; + } + + // Prepare arguments for mtpd. + String[] mtpd = null; + switch (profile.type) { + case VpnProfile.TYPE_PPTP: + mtpd = new String[] { + iface, "pptp", profile.server, "1723", + "name", profile.username, "password", profile.password, + "linkname", "vpn", "refuse-eap", "nodefaultroute", + "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", + (profile.mppe ? "+mppe" : "nomppe"), + }; + break; + case VpnProfile.TYPE_L2TP_IPSEC_PSK: + case VpnProfile.TYPE_L2TP_IPSEC_RSA: + mtpd = new String[] { + iface, "l2tp", profile.server, "1701", profile.l2tpSecret, + "name", profile.username, "password", profile.password, + "linkname", "vpn", "refuse-eap", "nodefaultroute", + "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", + }; + break; + } + + VpnConfig config = new VpnConfig(); + config.legacy = true; + config.user = profile.key; + config.interfaze = iface; + config.session = profile.name; + config.routes = profile.routes; + if (!profile.dnsServers.isEmpty()) { + config.dnsServers = Arrays.asList(profile.dnsServers.split(" +")); + } + if (!profile.searchDomains.isEmpty()) { + config.searchDomains = Arrays.asList(profile.searchDomains.split(" +")); + } + + startLegacyVpn(config, racoon, mtpd); + } + + private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { + stopLegacyVpn(); + // Prepare for the new request. This also checks the caller. prepare(null, VpnConfig.LEGACY_VPN); + updateState(DetailedState.CONNECTING, "startLegacyVpn"); // Start a new LegacyVpnRunner and we are done! mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd); mLegacyVpnRunner.start(); } + public synchronized void stopLegacyVpn() { + if (mLegacyVpnRunner != null) { + mLegacyVpnRunner.exit(); + mLegacyVpnRunner = null; + + synchronized (LegacyVpnRunner.TAG) { + // wait for old thread to completely finish before spinning up + // new instance, otherwise state updates can be out of order. + } + } + } + /** * Return the information of the current ongoing legacy VPN. */ public synchronized LegacyVpnInfo getLegacyVpnInfo() { // Check if the caller is authorized. enforceControlPermission(); - return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo(); + if (mLegacyVpnRunner == null) return null; + + final LegacyVpnInfo info = new LegacyVpnInfo(); + info.key = mLegacyVpnRunner.mConfig.user; + info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo); + if (mNetworkInfo.isConnected()) { + info.intent = mStatusIntent; + } + return info; + } + + public VpnConfig getLegacyVpnConfig() { + if (mLegacyVpnRunner != null) { + return mLegacyVpnRunner.mConfig; + } else { + return null; + } } /** @@ -407,8 +627,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private final String[] mDaemons; private final String[][] mArguments; private final LocalSocket[] mSockets; - private final String mOuterInterface; - private final LegacyVpnInfo mInfo; private long mTimer = -1; @@ -416,20 +634,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub { super(TAG); mConfig = config; mDaemons = new String[] {"racoon", "mtpd"}; + // TODO: clear arguments from memory once launched mArguments = new String[][] {racoon, mtpd}; mSockets = new LocalSocket[mDaemons.length]; - mInfo = new LegacyVpnInfo(); - - // This is the interface which VPN is running on. - mOuterInterface = mConfig.interfaze; - - // Legacy VPN is not a real package, so we use it to carry the key. - mInfo.key = mConfig.user; - mConfig.user = VpnConfig.LEGACY_VPN; } public void check(String interfaze) { - if (interfaze.equals(mOuterInterface)) { + if (interfaze.equals(mConfig.interfaze)) { Log.i(TAG, "Legacy VPN is going down with " + interfaze); exit(); } @@ -439,21 +650,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // We assume that everything is reset after stopping the daemons. interrupt(); for (LocalSocket socket : mSockets) { - try { - socket.close(); - } catch (Exception e) { - // ignore - } - } - } - - public LegacyVpnInfo getInfo() { - // Update the info when VPN is disconnected. - if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) { - mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED; - mInfo.intent = null; + IoUtils.closeQuietly(socket); } - return mInfo; + updateState(DetailedState.DISCONNECTED, "exit"); } @Override @@ -463,6 +662,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { synchronized (TAG) { Log.v(TAG, "Executing"); execute(); + monitorDaemons(); } } @@ -474,22 +674,21 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } else if (now - mTimer <= 60000) { Thread.sleep(yield ? 200 : 1); } else { - mInfo.state = LegacyVpnInfo.STATE_TIMEOUT; + updateState(DetailedState.FAILED, "checkpoint"); throw new IllegalStateException("Time is up"); } } private void execute() { // Catch all exceptions so we can clean up few things. + boolean initFinished = false; try { // Initialize the timer. checkpoint(false); - mInfo.state = LegacyVpnInfo.STATE_INITIALIZING; // Wait for the daemons to stop. for (String daemon : mDaemons) { - String key = "init.svc." + daemon; - while (!"stopped".equals(SystemProperties.get(key, "stopped"))) { + while (!SystemService.isStopped(daemon)) { checkpoint(true); } } @@ -501,6 +700,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { throw new IllegalStateException("Cannot delete the state"); } new File("/data/misc/vpn/abort").delete(); + initFinished = true; // Check if we need to restart any of the daemons. boolean restart = false; @@ -508,10 +708,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub { restart = restart || (arguments != null); } if (!restart) { - mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED; + updateState(DetailedState.DISCONNECTED, "execute"); return; } - mInfo.state = LegacyVpnInfo.STATE_CONNECTING; + updateState(DetailedState.CONNECTING, "execute"); // Start the daemon with arguments. for (int i = 0; i < mDaemons.length; ++i) { @@ -522,11 +722,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // Start the daemon. String daemon = mDaemons[i]; - SystemProperties.set("ctl.start", daemon); + SystemService.start(daemon); // Wait for the daemon to start. - String key = "init.svc." + daemon; - while (!"running".equals(SystemProperties.get(key))) { + while (!SystemService.isRunning(daemon)) { checkpoint(true); } @@ -582,8 +781,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { // Check if a running daemon is dead. for (int i = 0; i < mDaemons.length; ++i) { String daemon = mDaemons[i]; - if (mArguments[i] != null && !"running".equals( - SystemProperties.get("init.svc." + daemon))) { + if (mArguments[i] != null && !SystemService.isRunning(daemon)) { throw new IllegalStateException(daemon + " is dead"); } } @@ -640,26 +838,53 @@ public class Vpn extends INetworkManagementEventObserver.Stub { showNotification(mConfig, null, null); Log.i(TAG, "Connected!"); - mInfo.state = LegacyVpnInfo.STATE_CONNECTED; - mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null); + updateState(DetailedState.CONNECTED, "execute"); } } catch (Exception e) { Log.i(TAG, "Aborting", e); exit(); } finally { // Kill the daemons if they fail to stop. - if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) { + if (!initFinished) { for (String daemon : mDaemons) { - SystemProperties.set("ctl.stop", daemon); + SystemService.stop(daemon); } } // Do not leave an unstable state. - if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING || - mInfo.state == LegacyVpnInfo.STATE_CONNECTING) { - mInfo.state = LegacyVpnInfo.STATE_FAILED; + if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) { + updateState(DetailedState.FAILED, "execute"); } } } + + /** + * Monitor the daemons we started, moving to disconnected state if the + * underlying services fail. + */ + private void monitorDaemons() { + if (!mNetworkInfo.isConnected()) { + return; + } + + try { + while (true) { + Thread.sleep(2000); + for (int i = 0; i < mDaemons.length; i++) { + if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) { + return; + } + } + } + } catch (InterruptedException e) { + Log.d(TAG, "interrupted during monitorDaemons(); stopping services"); + } finally { + for (String daemon : mDaemons) { + SystemService.stop(daemon); + } + + updateState(DetailedState.DISCONNECTED, "babysit"); + } + } } } diff --git a/services/java/com/android/server/display/DisplayAdapter.java b/services/java/com/android/server/display/DisplayAdapter.java new file mode 100644 index 0000000..f9fa7a8 --- /dev/null +++ b/services/java/com/android/server/display/DisplayAdapter.java @@ -0,0 +1,48 @@ +/* + * 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.display; + +/** + * A display adapter makes zero or more display devices available to the system + * and provides facilities for discovering when displays are connected or disconnected. + * <p> + * For now, all display adapters are registered in the system server but + * in principle it could be done from other processes. + * </p> + */ +public abstract class DisplayAdapter { + /** + * Gets the display adapter name for debugging purposes. + * + * @return The display adapter name. + */ + public abstract String getName(); + + /** + * Registers the display adapter with the display manager. + * The display adapter should register any built-in display devices now. + * Other display devices can be registered dynamically later. + * + * @param listener The listener for callbacks. + */ + public abstract void register(Listener listener); + + public interface Listener { + public void onDisplayDeviceAdded(DisplayDevice device); + public void onDisplayDeviceRemoved(DisplayDevice device); + } +} diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java new file mode 100644 index 0000000..57002ff --- /dev/null +++ b/services/java/com/android/server/display/DisplayDevice.java @@ -0,0 +1,37 @@ +/* + * 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.display; + +/** + * Represents a physical display device such as the built-in display + * an external monitor, or a WiFi display. + */ +public abstract class DisplayDevice { + /** + * Gets the display adapter that makes the display device available. + * + * @return The display adapter. + */ + public abstract DisplayAdapter getAdapter(); + + /** + * Gets information about the display device. + * + * @param outInfo The object to populate with the information. + */ + public abstract void getInfo(DisplayDeviceInfo outInfo); +} diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java new file mode 100644 index 0000000..9c0f964 --- /dev/null +++ b/services/java/com/android/server/display/DisplayDeviceInfo.java @@ -0,0 +1,62 @@ +/* + * 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.display; + +/** + * Describes the characteristics of a physical display device. + */ +public final class DisplayDeviceInfo { + /** + * Gets the name of the display device, which may be derived from + * EDID or other sources. The name may be displayed to the user. + */ + public String name; + + /** + * The width of the display in its natural orientation, in pixels. + * This value is not affected by display rotation. + */ + public int width; + + /** + * The height of the display in its natural orientation, in pixels. + * This value is not affected by display rotation. + */ + public int height; + + public float refreshRate; + public int densityDpi; + public float xDpi; + public float yDpi; + + public void copyFrom(DisplayDeviceInfo other) { + name = other.name; + width = other.width; + height = other.height; + refreshRate = other.refreshRate; + densityDpi = other.densityDpi; + xDpi = other.xDpi; + yDpi = other.yDpi; + } + + // For debugging purposes + @Override + public String toString() { + return "\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, " + + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"; + } +} diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java new file mode 100644 index 0000000..2ebad1d --- /dev/null +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -0,0 +1,176 @@ +/* + * 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.display; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.display.DisplayManager; +import android.hardware.display.IDisplayManager; +import android.os.Binder; +import android.os.SystemProperties; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Surface; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Manages the properties, media routing and power state of attached displays. + * <p> + * The display manager service does not own or directly control the displays. + * Instead, other components in the system register their display adapters with the + * display manager service which acts as a central controller. + * </p> + */ +public final class DisplayManagerService extends IDisplayManager.Stub { + private static final String TAG = "DisplayManagerService"; + + private static final String SYSTEM_HEADLESS = "ro.config.headless"; + + private final Object mLock = new Object(); + + private final Context mContext; + private final boolean mHeadless; + + private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>(); + private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo(); + + public DisplayManagerService(Context context) { + mContext = context; + mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1"); + + registerDefaultDisplayAdapter(); + } + + private void registerDefaultDisplayAdapter() { + if (mHeadless) { + registerDisplayAdapter(new HeadlessDisplayAdapter(mContext)); + } else { + registerDisplayAdapter(new SurfaceFlingerDisplayAdapter(mContext)); + } + } + + // FIXME: this isn't the right API for the long term + public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) { + // hardcoded assuming 720p touch screen plugged into HDMI and USB + // need to redesign this + info.width = 1280; + info.height = 720; + } + + /** + * Returns true if the device is headless. + * + * @return True if the device is headless. + */ + public boolean isHeadless() { + return mHeadless; + } + + /** + * Save away new DisplayInfo data. + * @param displayId The local DisplayInfo to store the new data in. + * @param info The new data to be stored. + */ + public void setDisplayInfo(int displayId, DisplayInfo info) { + synchronized (mLock) { + if (displayId != Display.DEFAULT_DISPLAY) { + throw new UnsupportedOperationException(); + } + mDefaultDisplayInfo.copyFrom(info); + } + } + + /** + * Return requested DisplayInfo. + * @param displayId The data to retrieve. + * @param outInfo The structure to receive the data. + */ + @Override // Binder call + public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) { + synchronized (mLock) { + if (displayId != Display.DEFAULT_DISPLAY) { + return false; + } + outInfo.copyFrom(mDefaultDisplayInfo); + return true; + } + } + + private void registerDisplayAdapter(DisplayAdapter adapter) { + mDisplayAdapters.add(adapter); + adapter.register(new DisplayAdapter.Listener() { + @Override + public void onDisplayDeviceAdded(DisplayDevice device) { + DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); + device.getInfo(deviceInfo); + copyDisplayInfoFromDeviceInfo(mDefaultDisplayInfo, deviceInfo); + } + + @Override + public void onDisplayDeviceRemoved(DisplayDevice device) { + } + }); + } + + private void copyDisplayInfoFromDeviceInfo( + DisplayInfo displayInfo, DisplayDeviceInfo deviceInfo) { + // Bootstrap the logical display using the physical display. + displayInfo.appWidth = deviceInfo.width; + displayInfo.appHeight = deviceInfo.height; + displayInfo.logicalWidth = deviceInfo.width; + displayInfo.logicalHeight = deviceInfo.height; + displayInfo.rotation = Surface.ROTATION_0; + displayInfo.refreshRate = deviceInfo.refreshRate; + displayInfo.logicalDensityDpi = deviceInfo.densityDpi; + displayInfo.physicalXDpi = deviceInfo.xDpi; + displayInfo.physicalYDpi = deviceInfo.yDpi; + displayInfo.smallestNominalAppWidth = deviceInfo.width; + displayInfo.smallestNominalAppHeight = deviceInfo.height; + displayInfo.largestNominalAppWidth = deviceInfo.width; + displayInfo.largestNominalAppHeight = deviceInfo.height; + } + + @Override // Binder call + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext == null + || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump DisplayManager from from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + + pw.println("DISPLAY MANAGER (dumpsys display)\n"); + + pw.println("Headless: " + mHeadless); + + synchronized (mLock) { + for (DisplayAdapter adapter : mDisplayAdapters) { + pw.println("Adapter: " + adapter.getName()); + } + + pw.println("Default display info: " + mDefaultDisplayInfo); + } + + pw.println("Default display: " + + DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY)); + } +} diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java new file mode 100644 index 0000000..17c2360 --- /dev/null +++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java @@ -0,0 +1,62 @@ +/* + * 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.display; + +import android.content.Context; +import android.util.DisplayMetrics; + +/** + * Provides a fake default display for headless systems. + */ +public final class HeadlessDisplayAdapter extends DisplayAdapter { + private final Context mContext; + private final HeadlessDisplayDevice mDefaultDisplayDevice; + + public HeadlessDisplayAdapter(Context context) { + mContext = context; + mDefaultDisplayDevice = new HeadlessDisplayDevice(); + } + + @Override + public String getName() { + return "HeadlessDisplayAdapter"; + } + + @Override + public void register(Listener listener) { + listener.onDisplayDeviceAdded(mDefaultDisplayDevice); + } + + private final class HeadlessDisplayDevice extends DisplayDevice { + @Override + public DisplayAdapter getAdapter() { + return HeadlessDisplayAdapter.this; + } + + @Override + public void getInfo(DisplayDeviceInfo outInfo) { + outInfo.name = mContext.getResources().getString( + com.android.internal.R.string.display_manager_built_in_display); + outInfo.width = 640; + outInfo.height = 480; + outInfo.refreshRate = 60; + outInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT; + outInfo.xDpi = 160; + outInfo.yDpi = 160; + } + } +} diff --git a/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java b/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java new file mode 100644 index 0000000..9531acb --- /dev/null +++ b/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java @@ -0,0 +1,58 @@ +/* + * 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.display; + +import android.content.Context; + +/** + * A display adapter for the displays managed by Surface Flinger. + */ +public final class SurfaceFlingerDisplayAdapter extends DisplayAdapter { + private final Context mContext; + private final SurfaceFlingerDisplayDevice mDefaultDisplayDevice; + + private static native void nativeGetDefaultDisplayDeviceInfo(DisplayDeviceInfo outInfo); + + public SurfaceFlingerDisplayAdapter(Context context) { + mContext = context; + mDefaultDisplayDevice = new SurfaceFlingerDisplayDevice(); + } + + @Override + public String getName() { + return "SurfaceFlingerDisplayAdapter"; + } + + @Override + public void register(Listener listener) { + listener.onDisplayDeviceAdded(mDefaultDisplayDevice); + } + + private final class SurfaceFlingerDisplayDevice extends DisplayDevice { + @Override + public DisplayAdapter getAdapter() { + return SurfaceFlingerDisplayAdapter.this; + } + + @Override + public void getInfo(DisplayDeviceInfo outInfo) { + outInfo.name = mContext.getResources().getString( + com.android.internal.R.string.display_manager_built_in_display); + nativeGetDefaultDisplayDeviceInfo(outInfo); + } + } +} diff --git a/services/java/com/android/server/input/InputFilter.java b/services/java/com/android/server/input/InputFilter.java deleted file mode 100644 index 2ce0a02..0000000 --- a/services/java/com/android/server/input/InputFilter.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2011 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.input; - -import com.android.server.wm.WindowManagerService; - -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.view.InputEvent; -import android.view.InputEventConsistencyVerifier; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.WindowManagerPolicy; - -/** - * Filters input events before they are dispatched to the system. - * <p> - * At most one input filter can be installed by calling - * {@link WindowManagerService#setInputFilter}. When an input filter is installed, the - * system's behavior changes as follows: - * <ul> - * <li>Input events are first delivered to the {@link WindowManagerPolicy} - * interception methods before queuing as usual. This critical step takes care of managing - * the power state of the device and handling wake keys.</li> - * <li>Input events are then asynchronously delivered to the input filter's - * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to - * applications as usual. The input filter only receives input events that were - * generated by input device; the input filter will not receive input events that were - * injected into the system by other means, such as by instrumentation.</li> - * <li>The input filter processes and optionally transforms the stream of events. For example, - * it may transform a sequence of motion events representing an accessibility gesture into - * a different sequence of motion events, key presses or other system-level interactions. - * The input filter can send events to be dispatched by calling - * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the - * input event.</li> - * </ul> - * </p> - * <h3>The importance of input event consistency</h3> - * <p> - * The input filter mechanism is very low-level. At a minimum, it needs to ensure that it - * sends an internally consistent stream of input events to the dispatcher. There are - * very important invariants to be maintained. - * </p><p> - * For example, if a key down is sent, a corresponding key up should also be sent eventually. - * Likewise, for touch events, each pointer must individually go down with - * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then - * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP} - * and the sequence of pointer ids used must be consistent throughout the gesture. - * </p><p> - * Sometimes a filter may wish to cancel a previously dispatched key or motion. It should - * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly. - * </p><p> - * The input filter must take into account the fact that the input events coming from different - * devices or even different sources all consist of distinct streams of input. - * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify - * the source of the event and its semantics. There are be multiple sources of keys, - * touches and other input: they must be kept separate. - * </p> - * <h3>Policy flags</h3> - * <p> - * Input events received from the dispatcher and sent to the dispatcher have policy flags - * associated with them. Policy flags control some functions of the dispatcher. - * </p><p> - * The early policy interception decides whether an input event should be delivered - * to applications or dropped. The policy indicates its decision by setting the - * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} policy flag. The input filter may - * sometimes receive events that do not have this flag set. It should take note of - * the fact that the policy intends to drop the event, clean up its state, and - * then send appropriate cancellation events to the dispatcher if needed. - * </p><p> - * For example, suppose the input filter is processing a gesture and one of the touch events - * it receives does not have the {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag set. - * The input filter should clear its internal state about the gesture and then send key or - * motion events to the dispatcher to cancel any keys or pointers that are down. - * </p><p> - * Corollary: Events that set sent to the dispatcher should usually include the - * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped! - * </p><p> - * It may be prudent to disable automatic key repeating for synthetic key events - * by setting the {@link WindowManagerPolicy#FLAG_DISABLE_KEY_REPEAT} policy flag. - * </p> - */ -public abstract class InputFilter { - private static final int MSG_INSTALL = 1; - private static final int MSG_UNINSTALL = 2; - private static final int MSG_INPUT_EVENT = 3; - - private final H mH; - private Host mHost; - - // Consistency verifiers for debugging purposes. - private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier = - InputEventConsistencyVerifier.isInstrumentationEnabled() ? - new InputEventConsistencyVerifier(this, - InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, - "InputFilter#InboundInputEventConsistencyVerifier") : null; - private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier = - InputEventConsistencyVerifier.isInstrumentationEnabled() ? - new InputEventConsistencyVerifier(this, - InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, - "InputFilter#OutboundInputEventConsistencyVerifier") : null; - - /** - * Creates the input filter. - * - * @param looper The looper to run callbacks on. - */ - public InputFilter(Looper looper) { - mH = new H(looper); - } - - /** - * Called when the input filter is installed. - * This method is guaranteed to be non-reentrant. - * - * @param host The input filter host environment. - */ - final void install(Host host) { - mH.obtainMessage(MSG_INSTALL, host).sendToTarget(); - } - - /** - * Called when the input filter is uninstalled. - * This method is guaranteed to be non-reentrant. - */ - final void uninstall() { - mH.obtainMessage(MSG_UNINSTALL).sendToTarget(); - } - - /** - * Called to enqueue the input event for filtering. - * The event will be recycled after the input filter processes it. - * This method is guaranteed to be non-reentrant. - * - * @param event The input event to enqueue. - */ - final void filterInputEvent(InputEvent event, int policyFlags) { - mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget(); - } - - /** - * Sends an input event to the dispatcher. - * - * @param event The input event to publish. - * @param policyFlags The input event policy flags. - */ - public void sendInputEvent(InputEvent event, int policyFlags) { - if (event == null) { - throw new IllegalArgumentException("event must not be null"); - } - if (mHost == null) { - throw new IllegalStateException("Cannot send input event because the input filter " + - "is not installed."); - } - if (mOutboundInputEventConsistencyVerifier != null) { - mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0); - } - mHost.sendInputEvent(event, policyFlags); - } - - /** - * Called when an input event has been received from the dispatcher. - * <p> - * The default implementation sends the input event back to the dispatcher, unchanged. - * </p><p> - * The event will be recycled when this method returns. If you want to keep it around, - * make a copy! - * </p> - * - * @param event The input event that was received. - * @param policyFlags The input event policy flags. - */ - public void onInputEvent(InputEvent event, int policyFlags) { - sendInputEvent(event, policyFlags); - } - - /** - * Called when the filter is installed into the dispatch pipeline. - * <p> - * This method is called before the input filter receives any input events. - * The input filter should take this opportunity to prepare itself. - * </p> - */ - public void onInstalled() { - } - - /** - * Called when the filter is uninstalled from the dispatch pipeline. - * <p> - * This method is called after the input filter receives its last input event. - * The input filter should take this opportunity to clean up. - * </p> - */ - public void onUninstalled() { - } - - private final class H extends Handler { - public H(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_INSTALL: - mHost = (Host)msg.obj; - if (mInboundInputEventConsistencyVerifier != null) { - mInboundInputEventConsistencyVerifier.reset(); - } - if (mOutboundInputEventConsistencyVerifier != null) { - mOutboundInputEventConsistencyVerifier.reset(); - } - onInstalled(); - break; - - case MSG_UNINSTALL: - try { - onUninstalled(); - } finally { - mHost = null; - } - break; - - case MSG_INPUT_EVENT: { - final InputEvent event = (InputEvent)msg.obj; - try { - if (mInboundInputEventConsistencyVerifier != null) { - mInboundInputEventConsistencyVerifier.onInputEvent(event, 0); - } - onInputEvent(event, msg.arg1); - } finally { - event.recycle(); - } - break; - } - } - } - } - - interface Host { - public void sendInputEvent(InputEvent event, int policyFlags); - } -} diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java index bdd0aa4..29c68eb 100644 --- a/services/java/com/android/server/input/InputManagerService.java +++ b/services/java/com/android/server/input/InputManagerService.java @@ -42,8 +42,8 @@ import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.database.ContentObserver; -import android.hardware.input.IInputManager; import android.hardware.input.IInputDevicesChangedListener; +import android.hardware.input.IInputManager; import android.hardware.input.InputManager; import android.hardware.input.KeyboardLayout; import android.os.Binder; @@ -57,11 +57,12 @@ import android.os.Process; import android.os.RemoteException; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; -import android.server.BluetoothService; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; +import android.view.IInputFilter; +import android.view.IInputFilterHost; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; @@ -108,7 +109,6 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. private final Callbacks mCallbacks; private final InputManagerHandler mHandler; private boolean mSystemReady; - private BluetoothService mBluetoothService; private NotificationManager mNotificationManager; // Persistent data store. Must be locked each time during use. @@ -137,7 +137,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. // State for the currently installed input filter. final Object mInputFilterLock = new Object(); - InputFilter mInputFilter; // guarded by mInputFilterLock + IInputFilter mInputFilter; // guarded by mInputFilterLock InputFilterHost mInputFilterHost; // guarded by mInputFilterLock private static native int nativeInit(InputManagerService service, @@ -236,11 +236,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. updateShowTouchesFromSettings(); } - public void systemReady(BluetoothService bluetoothService) { + // TODO(BT) Pass in paramter for bluetooth system + public void systemReady() { if (DEBUG) { Slog.d(TAG, "System ready."); } - mBluetoothService = bluetoothService; mNotificationManager = (NotificationManager)mContext.getSystemService( Context.NOTIFICATION_SERVICE); mSystemReady = true; @@ -425,9 +425,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. * * @param filter The input filter, or null to remove the current filter. */ - public void setInputFilter(InputFilter filter) { + public void setInputFilter(IInputFilter filter) { synchronized (mInputFilterLock) { - final InputFilter oldFilter = mInputFilter; + final IInputFilter oldFilter = mInputFilter; if (oldFilter == filter) { return; // nothing to do } @@ -436,13 +436,21 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. mInputFilter = null; mInputFilterHost.disconnectLocked(); mInputFilterHost = null; - oldFilter.uninstall(); + try { + oldFilter.uninstall(); + } catch (RemoteException re) { + /* ignore */ + } } if (filter != null) { mInputFilter = filter; mInputFilterHost = new InputFilterHost(); - filter.install(mInputFilterHost); + try { + filter.install(mInputFilterHost); + } catch (RemoteException re) { + /* ignore */ + } } nativeSetInputFilterEnabled(mPtr, filter != null); @@ -1210,8 +1218,12 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. } // Native callback. - private void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { - mCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen); + private void notifySwitch(long whenNanos, int switchCode, int switchValue) { + switch (switchCode) { + case SW_LID: + mCallbacks.notifyLidSwitchChanged(whenNanos, switchValue == 0); + break; + } } // Native callback. @@ -1229,7 +1241,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. final boolean filterInputEvent(InputEvent event, int policyFlags) { synchronized (mInputFilterLock) { if (mInputFilter != null) { - mInputFilter.filterInputEvent(event, policyFlags); + try { + mInputFilter.filterInputEvent(event, policyFlags); + } catch (RemoteException e) { + /* ignore */ + } return false; } } @@ -1384,9 +1400,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. // Native callback. private String getDeviceAlias(String uniqueId) { - if (mBluetoothService != null && - BluetoothAdapter.checkBluetoothAddress(uniqueId)) { - return mBluetoothService.getRemoteAlias(uniqueId); + if (BluetoothAdapter.checkBluetoothAddress(uniqueId)) { + // TODO(BT) mBluetoothService.getRemoteAlias(uniqueId) + return null; } return null; } @@ -1422,6 +1438,10 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. * Private handler for the input manager. */ private final class InputManagerHandler extends Handler { + public InputManagerHandler() { + super(true /*async*/); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -1447,7 +1467,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. /** * Hosting interface for input filters to call back into the input manager. */ - private final class InputFilterHost implements InputFilter.Host { + private final class InputFilterHost extends IInputFilterHost.Stub { private boolean mDisconnected; public void disconnectLocked() { diff --git a/services/java/com/android/server/input/InputWindowHandle.java b/services/java/com/android/server/input/InputWindowHandle.java index 03d66af..ad4fdd1 100644 --- a/services/java/com/android/server/input/InputWindowHandle.java +++ b/services/java/com/android/server/input/InputWindowHandle.java @@ -87,12 +87,16 @@ public final class InputWindowHandle { // Window input features. public int inputFeatures; + // Display this input is on. + public final int displayId; + private native void nativeDispose(); public InputWindowHandle(InputApplicationHandle inputApplicationHandle, - Object windowState) { + Object windowState, int displayId) { this.inputApplicationHandle = inputApplicationHandle; this.windowState = windowState; + this.displayId = displayId; } @Override diff --git a/services/java/com/android/server/input/PersistentDataStore.java b/services/java/com/android/server/input/PersistentDataStore.java index fbe3e8b..71de776 100644 --- a/services/java/com/android/server/input/PersistentDataStore.java +++ b/services/java/com/android/server/input/PersistentDataStore.java @@ -16,7 +16,6 @@ package com.android.server.input; -import com.android.internal.os.AtomicFile; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; @@ -25,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java index 07f3125..7d030e9 100644 --- a/services/java/com/android/server/location/GeocoderProxy.java +++ b/services/java/com/android/server/location/GeocoderProxy.java @@ -16,92 +16,64 @@ package com.android.server.location; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.location.Address; import android.location.GeocoderParams; import android.location.IGeocodeProvider; -import android.os.IBinder; import android.os.RemoteException; -import android.os.SystemClock; import android.util.Log; +import com.android.server.ServiceWatcher; import java.util.List; /** - * A class for proxying IGeocodeProvider implementations. - * - * {@hide} + * Proxy for IGeocodeProvider implementations. */ public class GeocoderProxy { - private static final String TAG = "GeocoderProxy"; - public static final String SERVICE_ACTION = - "com.android.location.service.GeocodeProvider"; + private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider"; private final Context mContext; - private final Intent mIntent; - private final Object mMutex = new Object(); // synchronizes access to mServiceConnection - private Connection mServiceConnection; // never null after ctor - - public GeocoderProxy(Context context, String packageName) { - mContext = context; - mIntent = new Intent(SERVICE_ACTION); - reconnect(packageName); - } - - /** Bind to service. Will reconnect if already connected */ - public void reconnect(String packageName) { - synchronized (mMutex) { - if (mServiceConnection != null) { - mContext.unbindService(mServiceConnection); - } - mServiceConnection = new Connection(); - mIntent.setPackage(packageName); - mContext.bindService(mIntent, mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALLOW_OOM_MANAGEMENT); + private final ServiceWatcher mServiceWatcher; + + public static GeocoderProxy createAndBind(Context context, + List<String> initialPackageNames) { + GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames); + if (proxy.bind()) { + return proxy; + } else { + return null; } } - private class Connection implements ServiceConnection { + public GeocoderProxy(Context context, List<String> initialPackageNames) { + mContext = context; - private IGeocodeProvider mProvider; + mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames, + null, null); + } - public void onServiceConnected(ComponentName className, IBinder service) { - synchronized (this) { - mProvider = IGeocodeProvider.Stub.asInterface(service); - } - } + private boolean bind () { + return mServiceWatcher.start(); + } - public void onServiceDisconnected(ComponentName className) { - synchronized (this) { - mProvider = null; - } - } + private IGeocodeProvider getService() { + return IGeocodeProvider.Stub.asInterface(mServiceWatcher.getBinder()); + } - public IGeocodeProvider getProvider() { - synchronized (this) { - return mProvider; - } - } + public String getConnectedPackageName() { + return mServiceWatcher.getBestPackageName(); } public String getFromLocation(double latitude, double longitude, int maxResults, GeocoderParams params, List<Address> addrs) { - IGeocodeProvider provider; - synchronized (mMutex) { - provider = mServiceConnection.getProvider(); - } + IGeocodeProvider provider = getService(); if (provider != null) { try { - return provider.getFromLocation(latitude, longitude, maxResults, - params, addrs); + return provider.getFromLocation(latitude, longitude, maxResults, params, addrs); } catch (RemoteException e) { - Log.e(TAG, "getFromLocation failed", e); + Log.w(TAG, e); } } return "Service not Available"; @@ -111,19 +83,17 @@ public class GeocoderProxy { double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, GeocoderParams params, List<Address> addrs) { - IGeocodeProvider provider; - synchronized (mMutex) { - provider = mServiceConnection.getProvider(); - } + IGeocodeProvider provider = getService(); if (provider != null) { try { return provider.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults, params, addrs); } catch (RemoteException e) { - Log.e(TAG, "getFromLocationName failed", e); + Log.w(TAG, e); } } return "Service not Available"; } + } diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java new file mode 100644 index 0000000..26d9c15 --- /dev/null +++ b/services/java/com/android/server/location/GeofenceManager.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 20012 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.location; + +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import android.Manifest.permission; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.location.Geofence; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationRequest; +import android.os.Bundle; +import android.os.Looper; +import android.os.PowerManager; +import android.os.SystemClock; +import android.util.Log; + +import com.android.server.LocationManagerService; + +public class GeofenceManager implements LocationListener, PendingIntent.OnFinished { + private static final String TAG = "GeofenceManager"; + private static final boolean D = LocationManagerService.D; + + /** + * Assume a maximum land speed, as a heuristic to throttle location updates. + * (Air travel should result in an airplane mode toggle which will + * force a new location update anyway). + */ + private static final int MAX_SPEED_M_S = 100; // 360 km/hr (high speed train) + + private final Context mContext; + private final LocationManager mLocationManager; + private final PowerManager.WakeLock mWakeLock; + private final Looper mLooper; // looper thread to take location updates on + private final LocationBlacklist mBlacklist; + + 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) { + mContext = context; + mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); + PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + mLooper = Looper.myLooper(); + mBlacklist = blacklist; + + LocationRequest request = new LocationRequest() + .setQuality(LocationRequest.POWER_NONE) + .setFastestInterval(0); + mLocationManager.requestLocationUpdates(request, this, Looper.myLooper()); + } + + public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, int uid, + String packageName) { + GeofenceState state = new GeofenceState(geofence, mLastLocation, + request.getExpireAt(), packageName, intent); + + synchronized (mLock) { + // first make sure it doesn't already exist + for (int i = mFences.size() - 1; i >= 0; i--) { + GeofenceState w = mFences.get(i); + if (geofence.equals(w.mFence) && intent.equals(w.mIntent)) { + // already exists, remove the old one + mFences.remove(i); + break; + } + } + mFences.add(state); + updateProviderRequirementsLocked(); + } + } + + public void removeFence(Geofence fence, PendingIntent intent) { + synchronized (mLock) { + Iterator<GeofenceState> iter = mFences.iterator(); + while (iter.hasNext()) { + GeofenceState state = iter.next(); + if (state.mIntent.equals(intent)) { + + if (fence == null) { + // alwaus remove + iter.remove(); + } else { + // just remove matching fences + if (fence.equals(state.mFence)) { + iter.remove(); + } + } + } + } + updateProviderRequirementsLocked(); + } + } + + public void removeFence(String packageName) { + synchronized (mLock) { + Iterator<GeofenceState> iter = mFences.iterator(); + while (iter.hasNext()) { + GeofenceState state = iter.next(); + if (state.mPackageName.equals(packageName)) { + iter.remove(); + } + } + updateProviderRequirementsLocked(); + } + } + + private void removeExpiredFencesLocked() { + long time = SystemClock.elapsedRealtime(); + Iterator<GeofenceState> iter = mFences.iterator(); + while (iter.hasNext()) { + GeofenceState state = iter.next(); + if (state.mExpireAt < time) { + iter.remove(); + } + } + } + + private void processLocation(Location location) { + List<PendingIntent> enterIntents = new LinkedList<PendingIntent>(); + List<PendingIntent> exitIntents = new LinkedList<PendingIntent>(); + + synchronized (mLock) { + mLastLocation = location; + + removeExpiredFencesLocked(); + + for (GeofenceState state : mFences) { + if (mBlacklist.isBlacklisted(state.mPackageName)) { + if (D) Log.d(TAG, "skipping geofence processing for blacklisted app: " + + state.mPackageName); + continue; + } + + int event = state.processLocation(location); + if ((event & GeofenceState.FLAG_ENTER) != 0) { + enterIntents.add(state.mIntent); + } + if ((event & GeofenceState.FLAG_EXIT) != 0) { + exitIntents.add(state.mIntent); + } + } + updateProviderRequirementsLocked(); + } + + // release lock before sending intents + for (PendingIntent intent : exitIntents) { + sendIntentExit(intent); + } + for (PendingIntent intent : enterIntents) { + sendIntentEnter(intent); + } + } + + private void sendIntentEnter(PendingIntent pendingIntent) { + Intent intent = new Intent(); + intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true); + sendIntent(pendingIntent, intent); + } + + private void sendIntentExit(PendingIntent pendingIntent) { + Intent intent = new Intent(); + intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false); + sendIntent(pendingIntent, intent); + } + + private void sendIntent(PendingIntent pendingIntent, Intent intent) { + try { + mWakeLock.acquire(); + pendingIntent.send(mContext, 0, intent, this, null, permission.ACCESS_FINE_LOCATION); + } catch (PendingIntent.CanceledException e) { + removeFence(null, pendingIntent); + mWakeLock.release(); + } + } + + private void updateProviderRequirementsLocked() { + double minDistance = Double.MAX_VALUE; + for (GeofenceState state : mFences) { + if (state.getDistance() < minDistance) { + minDistance = state.getDistance(); + } + } + + if (minDistance == Double.MAX_VALUE) { + disableLocationLocked(); + } else { + int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S; + requestLocationLocked(intervalMs); + } + } + + private void requestLocationLocked(int intervalMs) { + mLocationManager.requestLocationUpdates(new LocationRequest().setInterval(intervalMs), this, + mLooper); + } + + private void disableLocationLocked() { + mLocationManager.removeUpdates(this); + } + + @Override + public void onLocationChanged(Location location) { + processLocation(location); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { } + + @Override + public void onProviderEnabled(String provider) { } + + @Override + public void onProviderDisabled(String provider) { } + + @Override + public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode, + String resultData, Bundle resultExtras) { + mWakeLock.release(); + } + + public void dump(PrintWriter pw) { + pw.println(" Geofences:"); + + for (GeofenceState state : mFences) { + pw.append(" "); + pw.append(state.mPackageName); + pw.append(" "); + pw.append(state.mFence.toString()); + pw.append("\n"); + } + } +} diff --git a/services/java/com/android/server/location/GeofenceState.java b/services/java/com/android/server/location/GeofenceState.java new file mode 100644 index 0000000..1fd737f --- /dev/null +++ b/services/java/com/android/server/location/GeofenceState.java @@ -0,0 +1,104 @@ +/* + * 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.location; + +import android.app.PendingIntent; +import android.location.Geofence; +import android.location.Location; + +/** + * Represents state associated with a geofence + */ +public class GeofenceState { + public final static int FLAG_ENTER = 0x01; + public final static int FLAG_EXIT = 0x02; + + private static final int STATE_UNKNOWN = 0; + private static final int STATE_INSIDE = 1; + private static final int STATE_OUTSIDE = 2; + + public final Geofence mFence; + private final Location mLocation; + public final long mExpireAt; + public final String mPackageName; + public final PendingIntent mIntent; + + int mState; // current state + double mDistance; // current distance to center of fence + + public GeofenceState(Geofence fence, Location prevLocation, long expireAt, + String packageName, PendingIntent intent) { + mState = STATE_UNKNOWN; + + mFence = fence; + mExpireAt = expireAt; + mPackageName = packageName; + mIntent = intent; + + mLocation = new Location(""); + mLocation.setLatitude(fence.getLatitude()); + mLocation.setLongitude(fence.getLongitude()); + + if (prevLocation != null) { + processLocation(prevLocation); + } + } + + /** + * Process a new location. + * @return FLAG_ENTER or FLAG_EXIT if the fence was crossed, 0 otherwise + */ + public int processLocation(Location location) { + mDistance = mLocation.distanceTo(location); + + int prevState = mState; + //TODO: inside/outside detection could be made more rigorous + boolean inside = mDistance <= Math.max(mFence.getRadius(), location.getAccuracy()); + if (inside) { + mState = STATE_INSIDE; + } else { + mState = STATE_OUTSIDE; + } + + if (prevState != 0 && mState != prevState) { + if (mState == STATE_INSIDE) return FLAG_ENTER; + if (mState == STATE_OUTSIDE) return FLAG_EXIT; + } + return 0; + } + + public double getDistance() { + return mDistance; + } + + @Override + public String toString() { + String state; + switch (mState) { + case STATE_INSIDE: + state = "IN"; + break; + case STATE_OUTSIDE: + state = "OUT"; + break; + default: + state = "?"; + } + return String.format("%s d=%.0f %s", mFence.toString(), mDistance, state); + } +} diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 4ad6140..c2c0a71 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -29,11 +29,13 @@ import android.location.IGpsStatusProvider; import android.location.ILocationManager; import android.location.INetInitiatedListener; import android.location.Location; +import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -54,17 +56,19 @@ import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.util.Log; import android.util.NtpTrustedTime; -import android.util.SparseIntArray; - import com.android.internal.app.IBatteryStats; import com.android.internal.location.GpsNetInitiatedHandler; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; +import java.io.PrintWriter; import java.io.StringReader; import java.util.ArrayList; import java.util.Date; @@ -81,8 +85,12 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final String TAG = "GpsLocationProvider"; - private static final boolean DEBUG = false; - private static final boolean VERBOSE = false; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private static final ProviderProperties PROPERTIES = new ProviderProperties( + true, true, false, false, true, true, true, + Criteria.POWER_HIGH, Criteria.ACCURACY_FINE); // these need to match GpsPositionMode enum in gps.h private static final int GPS_POSITION_MODE_STANDALONE = 0; @@ -150,14 +158,15 @@ public class GpsLocationProvider implements LocationProviderInterface { // Handler messages private static final int CHECK_LOCATION = 1; private static final int ENABLE = 2; - private static final int ENABLE_TRACKING = 3; + private static final int SET_REQUEST = 3; private static final int UPDATE_NETWORK_STATE = 4; private static final int INJECT_NTP_TIME = 5; private static final int DOWNLOAD_XTRA_DATA = 6; private static final int UPDATE_LOCATION = 7; private static final int ADD_LISTENER = 8; private static final int REMOVE_LISTENER = 9; - private static final int REQUEST_SINGLE_SHOT = 10; + private static final int INJECT_NTP_TIME_FINISHED = 10; + private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11; // Request setid private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1; @@ -179,6 +188,18 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final String PROPERTIES_FILE = "/etc/gps.conf"; + /** simpler wrapper for ProviderRequest + Worksource */ + private static class GpsRequest { + public ProviderRequest request; + public WorkSource source; + public GpsRequest(ProviderRequest request, WorkSource source) { + this.request = request; + this.source = source; + } + } + + private Object mLock = new Object(); + private int mLocationFlags = LOCATION_INVALID; // current status @@ -198,16 +219,28 @@ public class GpsLocationProvider implements LocationProviderInterface { // Typical hot TTTF is ~5 seconds, so 10 seconds seems sane. private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000; - // true if we are enabled - private volatile boolean mEnabled; - + // how often to request NTP time, in milliseconds + // current setting 24 hours + private static final long NTP_INTERVAL = 24*60*60*1000; + // how long to wait if we have a network error in NTP or XTRA downloading + // current setting - 5 minutes + private static final long RETRY_INTERVAL = 5*60*1000; + + // true if we are enabled, protected by this + private boolean mEnabled; + // true if we have network connectivity private boolean mNetworkAvailable; + // states for injecting ntp and downloading xtra data + private static final int STATE_PENDING_NETWORK = 0; + private static final int STATE_DOWNLOADING = 1; + private static final int STATE_IDLE = 2; + // flags to trigger NTP or XTRA data download when network becomes available // initialized to true so we do NTP and XTRA when the network comes up after booting - private boolean mInjectNtpTimePending = true; - private boolean mDownloadXtraDataPending = true; + private int mInjectNtpTimePending = STATE_PENDING_NETWORK; + private int mDownloadXtraDataPending = STATE_PENDING_NETWORK; // set to true if the GPS engine does not do on-demand NTP time requests private boolean mPeriodicTimeInjection; @@ -217,16 +250,13 @@ public class GpsLocationProvider implements LocationProviderInterface { // true if GPS engine is on private boolean mEngineOn; - + // requested frequency of fixes, in milliseconds private int mFixInterval = 1000; // true if we started navigation private boolean mStarted; - // true if single shot request is in progress - private boolean mSingleShot; - // capabilities of the GPS engine private int mEngineCapabilities; @@ -236,7 +266,7 @@ public class GpsLocationProvider implements LocationProviderInterface { // for calculating time to first fix private long mFixRequestTime = 0; // time to first fix for most recent session - private int mTTFF = 0; + private int mTimeToFirstFix = 0; // time we received our last fix private long mLastFixTime; @@ -251,7 +281,7 @@ public class GpsLocationProvider implements LocationProviderInterface { private final Context mContext; private final NtpTrustedTime mNtpTime; - private final ILocationManager mLocationManager; + private final ILocationManager mILocationManager; private Location mLocation = new Location(LocationManager.GPS_PROVIDER); private Bundle mLocationExtras = new Bundle(); private ArrayList<Listener> mListeners = new ArrayList<Listener>(); @@ -267,17 +297,11 @@ public class GpsLocationProvider implements LocationProviderInterface { private int mAGpsDataConnectionState; private int mAGpsDataConnectionIpAddr; private final ConnectivityManager mConnMgr; - private final GpsNetInitiatedHandler mNIHandler; + private final GpsNetInitiatedHandler mNIHandler; // Wakelocks private final static String WAKELOCK_KEY = "GpsLocationProvider"; private final PowerManager.WakeLock mWakeLock; - // bitfield of pending messages to our Handler - // used only for messages that cannot have multiple instances queued - private int mPendingMessageBits; - // separate counter for ADD_LISTENER and REMOVE_LISTENER messages, - // which might have multiple instances queued - private int mPendingListenerMessages; // Alarms private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP"; @@ -287,22 +311,18 @@ public class GpsLocationProvider implements LocationProviderInterface { private final PendingIntent mTimeoutIntent; private final IBatteryStats mBatteryStats; - private final SparseIntArray mClientUids = new SparseIntArray(); - // how often to request NTP time, in milliseconds - // current setting 24 hours - private static final long NTP_INTERVAL = 24*60*60*1000; - // how long to wait if we have a network error in NTP or XTRA downloading - // current setting - 5 minutes - private static final long RETRY_INTERVAL = 5*60*1000; + // only modified on handler thread + private int[] mClientUids = new int[0]; private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() { + @Override public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException { if (listener == null) { throw new NullPointerException("listener is null in addGpsStatusListener"); } - synchronized(mListeners) { + synchronized (mListeners) { IBinder binder = listener.asBinder(); int size = mListeners.size(); for (int i = 0; i < size; i++) { @@ -319,12 +339,13 @@ public class GpsLocationProvider implements LocationProviderInterface { } } + @Override public void removeGpsStatusListener(IGpsStatusListener listener) { if (listener == null) { throw new NullPointerException("listener is null in addGpsStatusListener"); } - synchronized(mListeners) { + synchronized (mListeners) { IBinder binder = listener.asBinder(); Listener l = null; int size = mListeners.size(); @@ -353,7 +374,7 @@ public class GpsLocationProvider implements LocationProviderInterface { if (action.equals(ALARM_WAKEUP)) { if (DEBUG) Log.d(TAG, "ALARM_WAKEUP"); - startNavigating(false); + startNavigating(); } else if (action.equals(ALARM_TIMEOUT)) { if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT"); hibernate(); @@ -361,6 +382,22 @@ public class GpsLocationProvider implements LocationProviderInterface { checkSmsSuplInit(intent); } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) { checkWapSuplInit(intent); + } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + int networkState; + if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) { + networkState = LocationProvider.TEMPORARILY_UNAVAILABLE; + } else { + networkState = LocationProvider.AVAILABLE; + } + + // retrieve NetworkInfo result for this UID + NetworkInfo info = + intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + ConnectivityManager connManager = (ConnectivityManager) + mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + info = connManager.getNetworkInfo(info.getType()); + + updateNetworkState(networkState, info); } } }; @@ -382,10 +419,10 @@ public class GpsLocationProvider implements LocationProviderInterface { return native_is_supported(); } - public GpsLocationProvider(Context context, ILocationManager locationManager) { + public GpsLocationProvider(Context context, ILocationManager ilocationManager) { mContext = context; mNtpTime = NtpTrustedTime.getInstance(context); - mLocationManager = locationManager; + mILocationManager = ilocationManager; mNIHandler = new GpsNetInitiatedHandler(context); mLocation.setExtras(mLocationExtras); @@ -393,7 +430,7 @@ public class GpsLocationProvider implements LocationProviderInterface { // Create a wake lock PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); - mWakeLock.setReferenceCounted(false); + mWakeLock.setReferenceCounted(true); mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0); @@ -467,22 +504,21 @@ public class GpsLocationProvider implements LocationProviderInterface { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ALARM_WAKEUP); intentFilter.addAction(ALARM_TIMEOUT); + intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); mContext.registerReceiver(mBroadcastReciever, intentFilter); } /** * Returns the name of this provider. */ + @Override public String getName() { return LocationManager.GPS_PROVIDER; } - /** - * Returns true if the provider requires access to a - * data network (e.g., the Internet), false otherwise. - */ - public boolean requiresNetwork() { - return true; + @Override + public ProviderProperties getProperties() { + return PROPERTIES; } public void updateNetworkState(int state, NetworkInfo info) { @@ -516,7 +552,7 @@ public class GpsLocationProvider implements LocationProviderInterface { String apnName = info.getExtraInfo(); if (mNetworkAvailable) { if (apnName == null) { - /* Assign a dummy value in the case of C2K as otherwise we will have a runtime + /* Assign a dummy value in the case of C2K as otherwise we will have a runtime exception in the following call to native_agps_data_conn_open*/ apnName = "dummy-apn"; } @@ -541,88 +577,111 @@ public class GpsLocationProvider implements LocationProviderInterface { } if (mNetworkAvailable) { - if (mInjectNtpTimePending) { + if (mInjectNtpTimePending == STATE_PENDING_NETWORK) { sendMessage(INJECT_NTP_TIME, 0, null); } - if (mDownloadXtraDataPending) { + if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) { sendMessage(DOWNLOAD_XTRA_DATA, 0, null); } } } private void handleInjectNtpTime() { + if (mInjectNtpTimePending == STATE_DOWNLOADING) { + // already downloading data + return; + } if (!mNetworkAvailable) { // try again when network is up - mInjectNtpTimePending = true; + mInjectNtpTimePending = STATE_PENDING_NETWORK; return; } - mInjectNtpTimePending = false; + mInjectNtpTimePending = STATE_DOWNLOADING; - long delay; + // hold wake lock while task runs + mWakeLock.acquire(); + AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { + @Override + public void run() { + long delay; - // force refresh NTP cache when outdated - if (mNtpTime.getCacheAge() >= NTP_INTERVAL) { - mNtpTime.forceRefresh(); - } + // force refresh NTP cache when outdated + if (mNtpTime.getCacheAge() >= NTP_INTERVAL) { + mNtpTime.forceRefresh(); + } - // only update when NTP time is fresh - if (mNtpTime.getCacheAge() < NTP_INTERVAL) { - long time = mNtpTime.getCachedNtpTime(); - long timeReference = mNtpTime.getCachedNtpTimeReference(); - long certainty = mNtpTime.getCacheCertainty(); - long now = System.currentTimeMillis(); - - Log.d(TAG, "NTP server returned: " - + time + " (" + new Date(time) - + ") reference: " + timeReference - + " certainty: " + certainty - + " system time offset: " + (time - now)); - - native_inject_time(time, timeReference, (int) certainty); - delay = NTP_INTERVAL; - } else { - if (DEBUG) Log.d(TAG, "requestTime failed"); - delay = RETRY_INTERVAL; - } + // only update when NTP time is fresh + if (mNtpTime.getCacheAge() < NTP_INTERVAL) { + long time = mNtpTime.getCachedNtpTime(); + long timeReference = mNtpTime.getCachedNtpTimeReference(); + long certainty = mNtpTime.getCacheCertainty(); + long now = System.currentTimeMillis(); + + Log.d(TAG, "NTP server returned: " + + time + " (" + new Date(time) + + ") reference: " + timeReference + + " certainty: " + certainty + + " system time offset: " + (time - now)); + + native_inject_time(time, timeReference, (int) certainty); + delay = NTP_INTERVAL; + } else { + if (DEBUG) Log.d(TAG, "requestTime failed"); + delay = RETRY_INTERVAL; + } - if (mPeriodicTimeInjection) { - // send delayed message for next NTP injection - // since this is delayed and not urgent we do not hold a wake lock here - mHandler.removeMessages(INJECT_NTP_TIME); - mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); - } + sendMessage(INJECT_NTP_TIME_FINISHED, 0, null); + + if (mPeriodicTimeInjection) { + // send delayed message for next NTP injection + // since this is delayed and not urgent we do not hold a wake lock here + mHandler.sendEmptyMessageDelayed(INJECT_NTP_TIME, delay); + } + + // release wake lock held by task + mWakeLock.release(); + } + }); } private void handleDownloadXtraData() { + if (mDownloadXtraDataPending == STATE_DOWNLOADING) { + // already downloading data + return; + } if (!mNetworkAvailable) { // try again when network is up - mDownloadXtraDataPending = true; + mDownloadXtraDataPending = STATE_PENDING_NETWORK; return; } - mDownloadXtraDataPending = false; + mDownloadXtraDataPending = STATE_DOWNLOADING; + + // hold wake lock while task runs + mWakeLock.acquire(); + AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { + @Override + public void run() { + GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties); + byte[] data = xtraDownloader.downloadXtraData(); + if (data != null) { + if (DEBUG) { + Log.d(TAG, "calling native_inject_xtra_data"); + } + native_inject_xtra_data(data, data.length); + } + sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null); - GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties); - byte[] data = xtraDownloader.downloadXtraData(); - if (data != null) { - if (DEBUG) { - Log.d(TAG, "calling native_inject_xtra_data"); - } - native_inject_xtra_data(data, data.length); - } else { - // try again later - // since this is delayed and not urgent we do not hold a wake lock here - mHandler.removeMessages(DOWNLOAD_XTRA_DATA); - mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL); - } - } + if (data == null) { + // try again later + // since this is delayed and not urgent we do not hold a wake lock here + mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA, RETRY_INTERVAL); + } - /** - * This is called to inform us when another location provider returns a location. - * Someday we might use this for network location injection to aid the GPS - */ - public void updateLocation(Location location) { - sendMessage(UPDATE_LOCATION, 0, location); + // release wake lock held by task + mWakeLock.release(); + } + }); } private void handleUpdateLocation(Location location) { @@ -633,107 +692,26 @@ public class GpsLocationProvider implements LocationProviderInterface { } /** - * Returns true if the provider requires access to a - * satellite-based positioning system (e.g., GPS), false - * otherwise. - */ - public boolean requiresSatellite() { - return true; - } - - /** - * Returns true if the provider requires access to an appropriate - * cellular network (e.g., to make use of cell tower IDs), false - * otherwise. - */ - public boolean requiresCell() { - return false; - } - - /** - * Returns true if the use of this provider may result in a - * monetary charge to the user, false if use is free. It is up to - * each provider to give accurate information. - */ - public boolean hasMonetaryCost() { - return false; - } - - /** - * Returns true if the provider is able to provide altitude - * information, false otherwise. A provider that reports altitude - * under most circumstances but may occassionally not report it - * should return true. - */ - public boolean supportsAltitude() { - return true; - } - - /** - * Returns true if the provider is able to provide speed - * information, false otherwise. A provider that reports speed - * under most circumstances but may occassionally not report it - * should return true. - */ - public boolean supportsSpeed() { - return true; - } - - /** - * Returns true if the provider is able to provide bearing - * information, false otherwise. A provider that reports bearing - * under most circumstances but may occassionally not report it - * should return true. - */ - public boolean supportsBearing() { - return true; - } - - /** - * Returns the power requirement for this provider. - * - * @return the power requirement for this provider, as one of the - * constants Criteria.POWER_REQUIREMENT_*. - */ - public int getPowerRequirement() { - return Criteria.POWER_HIGH; - } - - /** - * Returns true if this provider meets the given criteria, - * false otherwise. - */ - public boolean meetsCriteria(Criteria criteria) { - return (criteria.getPowerRequirement() != Criteria.POWER_LOW); - } - - /** - * Returns the horizontal accuracy of this provider - * - * @return the accuracy of location from this provider, as one - * of the constants Criteria.ACCURACY_*. - */ - public int getAccuracy() { - return Criteria.ACCURACY_FINE; - } - - /** * Enables this provider. When enabled, calls to getStatus() * must be handled. Hardware may be started up * when the provider is enabled. */ + @Override public void enable() { - synchronized (mHandler) { - sendMessage(ENABLE, 1, null); - } + sendMessage(ENABLE, 1, null); } private void handleEnable() { if (DEBUG) Log.d(TAG, "handleEnable"); - if (mEnabled) return; - mEnabled = native_init(); - if (mEnabled) { + synchronized (mLock) { + if (mEnabled) return; + mEnabled = true; + } + + boolean enabled = native_init(); + + if (enabled) { mSupportsXtra = native_supports_xtra(); if (mSuplServerHost != null) { native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort); @@ -742,6 +720,9 @@ public class GpsLocationProvider implements LocationProviderInterface { native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort); } } else { + synchronized (mLock) { + mEnabled = false; + } Log.w(TAG, "Failed to enable location provider"); } } @@ -751,27 +732,35 @@ public class GpsLocationProvider implements LocationProviderInterface { * need not be handled. Hardware may be shut * down while the provider is disabled. */ + @Override public void disable() { - synchronized (mHandler) { - sendMessage(ENABLE, 0, null); - } + sendMessage(ENABLE, 0, null); } private void handleDisable() { if (DEBUG) Log.d(TAG, "handleDisable"); - if (!mEnabled) return; - mEnabled = false; + synchronized (mLock) { + if (!mEnabled) return; + mEnabled = false; + } + stopNavigating(); + mAlarmManager.cancel(mWakeupIntent); + mAlarmManager.cancel(mTimeoutIntent); // do this before releasing wakelock native_cleanup(); } + @Override public boolean isEnabled() { - return mEnabled; + synchronized (mLock) { + return mEnabled; + } } + @Override public int getStatus(Bundle extras) { if (extras != null) { extras.putInt("satellites", mSvCount); @@ -788,93 +777,69 @@ public class GpsLocationProvider implements LocationProviderInterface { } } + @Override public long getStatusUpdateTime() { return mStatusUpdateTime; } - public void enableLocationTracking(boolean enable) { - // FIXME - should set a flag here to avoid race conditions with single shot request - synchronized (mHandler) { - sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null); - } + @Override + public void setRequest(ProviderRequest request, WorkSource source) { + sendMessage(SET_REQUEST, 0, new GpsRequest(request, source)); } - private void handleEnableLocationTracking(boolean enable) { - if (enable) { - mTTFF = 0; - mLastFixTime = 0; - startNavigating(false); - } else { - if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) { - mAlarmManager.cancel(mWakeupIntent); - mAlarmManager.cancel(mTimeoutIntent); - } - stopNavigating(); - } - } + private void handleSetRequest(ProviderRequest request, WorkSource source) { + if (DEBUG) Log.d(TAG, "setRequest " + request); - public boolean requestSingleShotFix() { - if (mStarted) { - // cannot do single shot if already navigating - return false; - } - synchronized (mHandler) { - mHandler.removeMessages(REQUEST_SINGLE_SHOT); - Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT); - mHandler.sendMessage(m); - } - return true; - } - private void handleRequestSingleShot() { - mTTFF = 0; - mLastFixTime = 0; - startNavigating(true); - } - public void setMinTime(long minTime, WorkSource ws) { - if (DEBUG) Log.d(TAG, "setMinTime " + minTime); - - if (minTime >= 0) { - mFixInterval = (int)minTime; + if (request.reportLocation) { + // update client uids + int[] uids = new int[source.size()]; + for (int i=0; i < source.size(); i++) { + uids[i] = source.get(i); + } + updateClientUids(uids); + + mFixInterval = (int) request.interval; + // check for overflow + if (mFixInterval != request.interval) { + Log.w(TAG, "interval overflow: " + request.interval); + mFixInterval = Integer.MAX_VALUE; + } + + // apply request to GPS engine if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) { + // change period if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC, mFixInterval, 0, 0)) { Log.e(TAG, "set_position_mode failed in setMinTime()"); } + } else if (!mStarted) { + // start GPS + startNavigating(); } - } - } - - public String getInternalState() { - StringBuilder s = new StringBuilder(); - s.append(" mFixInterval=").append(mFixInterval).append("\n"); - s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" ("); - if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED "); - if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB "); - if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA "); - if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT "); - if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME "); - s.append(")\n"); + } else { + updateClientUids(new int[0]); - s.append(native_get_internal_state()); - return s.toString(); + stopNavigating(); + mAlarmManager.cancel(mWakeupIntent); + mAlarmManager.cancel(mTimeoutIntent); + } } private final class Listener implements IBinder.DeathRecipient { final IGpsStatusListener mListener; - - int mSensors = 0; - + Listener(IGpsStatusListener listener) { mListener = listener; } - + + @Override public void binderDied() { if (DEBUG) Log.d(TAG, "GPS status listener died"); - synchronized(mListeners) { + synchronized (mListeners) { mListeners.remove(this); } if (mListener != null) { @@ -883,64 +848,50 @@ public class GpsLocationProvider implements LocationProviderInterface { } } - public void addListener(int uid) { - synchronized (mWakeLock) { - mPendingListenerMessages++; - mWakeLock.acquire(); - Message m = Message.obtain(mHandler, ADD_LISTENER); - m.arg1 = uid; - mHandler.sendMessage(m); - } - } - - private void handleAddListener(int uid) { - synchronized(mListeners) { - if (mClientUids.indexOfKey(uid) >= 0) { - // Shouldn't be here -- already have this uid. - Log.w(TAG, "Duplicate add listener for uid " + uid); - return; + private void updateClientUids(int[] uids) { + // Find uid's that were not previously tracked + for (int uid1 : uids) { + boolean newUid = true; + for (int uid2 : mClientUids) { + if (uid1 == uid2) { + newUid = false; + break; + } } - mClientUids.put(uid, 0); - if (mNavigating) { + if (newUid) { try { - mBatteryStats.noteStartGps(uid); + mBatteryStats.noteStartGps(uid1); } catch (RemoteException e) { - Log.w(TAG, "RemoteException in addListener"); + Log.w(TAG, "RemoteException", e); } } } - } - - public void removeListener(int uid) { - synchronized (mWakeLock) { - mPendingListenerMessages++; - mWakeLock.acquire(); - Message m = Message.obtain(mHandler, REMOVE_LISTENER); - m.arg1 = uid; - mHandler.sendMessage(m); - } - } - private void handleRemoveListener(int uid) { - synchronized(mListeners) { - if (mClientUids.indexOfKey(uid) < 0) { - // Shouldn't be here -- don't have this uid. - Log.w(TAG, "Unneeded remove listener for uid " + uid); - return; + // Find uid'd that were tracked but have now disappeared + for (int uid1 : mClientUids) { + boolean oldUid = true; + for (int uid2 : uids) { + if (uid1 == uid2) { + oldUid = false; + break; + } } - mClientUids.delete(uid); - if (mNavigating) { + if (oldUid) { try { - mBatteryStats.noteStopGps(uid); + mBatteryStats.noteStopGps(uid1); } catch (RemoteException e) { - Log.w(TAG, "RemoteException in removeListener"); + Log.w(TAG, "RemoteException", e); } } } + + // save current uids + mClientUids = uids; } + @Override public boolean sendExtraCommand(String command, Bundle extras) { - + long identity = Binder.clearCallingIdentity(); boolean result = false; @@ -957,7 +908,7 @@ public class GpsLocationProvider implements LocationProviderInterface { } else { Log.w(TAG, "sendExtraCommand: unknown command " + command); } - + Binder.restoreCallingIdentity(identity); return result; } @@ -992,18 +943,17 @@ public class GpsLocationProvider implements LocationProviderInterface { return false; } - private void startNavigating(boolean singleShot) { + private void startNavigating() { if (!mStarted) { if (DEBUG) Log.d(TAG, "startNavigating"); + mTimeToFirstFix = 0; + mLastFixTime = 0; mStarted = true; - mSingleShot = singleShot; mPositionMode = GPS_POSITION_MODE_STANDALONE; if (Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) { - if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) { - mPositionMode = GPS_POSITION_MODE_MS_ASSISTED; - } else if (hasCapability(GPS_CAPABILITY_MSB)) { + if (hasCapability(GPS_CAPABILITY_MSB)) { mPositionMode = GPS_POSITION_MODE_MS_BASED; } } @@ -1039,9 +989,8 @@ public class GpsLocationProvider implements LocationProviderInterface { if (DEBUG) Log.d(TAG, "stopNavigating"); if (mStarted) { mStarted = false; - mSingleShot = false; native_stop(); - mTTFF = 0; + mTimeToFirstFix = 0; mLastFixTime = 0; mLocationFlags = LOCATION_INVALID; @@ -1056,8 +1005,7 @@ public class GpsLocationProvider implements LocationProviderInterface { mAlarmManager.cancel(mTimeoutIntent); mAlarmManager.cancel(mWakeupIntent); long now = SystemClock.elapsedRealtime(); - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent); } private boolean hasCapability(int capability) { @@ -1078,6 +1026,9 @@ public class GpsLocationProvider implements LocationProviderInterface { mLocation.setLatitude(latitude); mLocation.setLongitude(longitude); mLocation.setTime(timestamp); + // It would be nice to push the elapsed real-time timestamp + // further down the stack, but this is still useful + mLocation.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano()); } if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) { mLocation.setAltitude(altitude); @@ -1102,7 +1053,7 @@ public class GpsLocationProvider implements LocationProviderInterface { mLocation.setExtras(mLocationExtras); try { - mLocationManager.reportLocation(mLocation, false); + mILocationManager.reportLocation(mLocation, false); } catch (RemoteException e) { Log.e(TAG, "RemoteException calling reportLocation"); } @@ -1110,17 +1061,17 @@ public class GpsLocationProvider implements LocationProviderInterface { mLastFixTime = System.currentTimeMillis(); // report time to first fix - if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { - mTTFF = (int)(mLastFixTime - mFixRequestTime); - if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF); + if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { + mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime); + if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix); // notify status listeners - synchronized(mListeners) { + synchronized (mListeners) { int size = mListeners.size(); for (int i = 0; i < size; i++) { Listener listener = mListeners.get(i); try { - listener.mListener.onFirstFix(mTTFF); + listener.mListener.onFirstFix(mTimeToFirstFix); } catch (RemoteException e) { Log.w(TAG, "RemoteException in stopNavigating"); mListeners.remove(listener); @@ -1131,9 +1082,6 @@ public class GpsLocationProvider implements LocationProviderInterface { } } - if (mSingleShot) { - stopNavigating(); - } if (mStarted && mStatus != LocationProvider.AVAILABLE) { // we want to time out if we do not receive a fix // within the time out and we are requesting infrequent fixes @@ -1161,7 +1109,7 @@ public class GpsLocationProvider implements LocationProviderInterface { private void reportStatus(int status) { if (DEBUG) Log.v(TAG, "reportStatus status: " + status); - synchronized(mListeners) { + synchronized (mListeners) { boolean wasNavigating = mNavigating; switch (status) { @@ -1199,20 +1147,6 @@ public class GpsLocationProvider implements LocationProviderInterface { } } - try { - // update battery stats - for (int i=mClientUids.size() - 1; i >= 0; i--) { - int uid = mClientUids.keyAt(i); - if (mNavigating) { - mBatteryStats.noteStartGps(uid); - } else { - mBatteryStats.noteStopGps(uid); - } - } - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in reportStatus"); - } - // send an intent to notify that the GPS has been enabled or disabled. Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION); intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating); @@ -1227,15 +1161,15 @@ public class GpsLocationProvider implements LocationProviderInterface { private void reportSvStatus() { int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks); - - synchronized(mListeners) { + + synchronized (mListeners) { int size = mListeners.size(); for (int i = 0; i < size; i++) { Listener listener = mListeners.get(i); try { - listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs, - mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK], - mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]); + listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs, + mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK], + mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]); } catch (RemoteException e) { Log.w(TAG, "RemoteException in reportSvInfo"); mListeners.remove(listener); @@ -1251,7 +1185,7 @@ public class GpsLocationProvider implements LocationProviderInterface { " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK])); for (int i = 0; i < svCount; i++) { Log.v(TAG, "sv: " + mSvs[i] + - " snr: " + (float)mSnrs[i]/10 + + " snr: " + mSnrs[i]/10 + " elev: " + mSvElevations[i] + " azimuth: " + mSvAzimuths[i] + ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") + @@ -1339,7 +1273,7 @@ public class GpsLocationProvider implements LocationProviderInterface { * called from native code to report NMEA data received */ private void reportNmea(long timestamp) { - synchronized(mListeners) { + synchronized (mListeners) { int size = mListeners.size(); if (size > 0) { // don't bother creating the String if we have no listeners @@ -1386,19 +1320,18 @@ public class GpsLocationProvider implements LocationProviderInterface { //============================================================= private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() { // Sends a response for an NI reqeust to HAL. + @Override public boolean sendNiResponse(int notificationId, int userResponse) { // TODO Add Permission check - StringBuilder extrasBuf = new StringBuilder(); - if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId + ", response: " + userResponse); native_send_ni_response(notificationId, userResponse); return true; } }; - + public INetInitiatedListener getNetInitiatedListener() { return mNetInitiatedListener; } @@ -1547,19 +1480,18 @@ public class GpsLocationProvider implements LocationProviderInterface { } private void sendMessage(int message, int arg, Object obj) { - // hold a wake lock while messages are pending - synchronized (mWakeLock) { - mPendingMessageBits |= (1 << message); - mWakeLock.acquire(); - mHandler.removeMessages(message); - Message m = Message.obtain(mHandler, message); - m.arg1 = arg; - m.obj = obj; - mHandler.sendMessage(m); - } + // hold a wake lock until this message is delivered + // note that this assumes the message will not be removed from the queue before + // it is handled (otherwise the wake lock would be leaked). + mWakeLock.acquire(); + mHandler.obtainMessage(message, arg, 1, obj).sendToTarget(); } private final class ProviderHandler extends Handler { + public ProviderHandler() { + super(true /*async*/); + } + @Override public void handleMessage(Message msg) { int message = msg.what; @@ -1571,11 +1503,9 @@ public class GpsLocationProvider implements LocationProviderInterface { handleDisable(); } break; - case ENABLE_TRACKING: - handleEnableLocationTracking(msg.arg1 == 1); - break; - case REQUEST_SINGLE_SHOT: - handleRequestSingleShot(); + case SET_REQUEST: + GpsRequest gpsRequest = (GpsRequest) msg.obj; + handleSetRequest(gpsRequest.request, gpsRequest.source); break; case UPDATE_NETWORK_STATE: handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj); @@ -1588,25 +1518,19 @@ public class GpsLocationProvider implements LocationProviderInterface { handleDownloadXtraData(); } break; - case UPDATE_LOCATION: - handleUpdateLocation((Location)msg.obj); + case INJECT_NTP_TIME_FINISHED: + mInjectNtpTimePending = STATE_IDLE; break; - case ADD_LISTENER: - handleAddListener(msg.arg1); + case DOWNLOAD_XTRA_DATA_FINISHED: + mDownloadXtraDataPending = STATE_IDLE; break; - case REMOVE_LISTENER: - handleRemoveListener(msg.arg1); + case UPDATE_LOCATION: + handleUpdateLocation((Location)msg.obj); break; } - // release wake lock if no messages are pending - synchronized (mWakeLock) { - mPendingMessageBits &= ~(1 << message); - if (message == ADD_LISTENER || message == REMOVE_LISTENER) { - mPendingListenerMessages--; - } - if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) { - mWakeLock.release(); - } + if (msg.arg2 == 1) { + // wakelock was taken for this message, release it + mWakeLock.release(); } } }; @@ -1617,17 +1541,39 @@ public class GpsLocationProvider implements LocationProviderInterface { super("GpsLocationProvider"); } + @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); initialize(); Looper.prepare(); + + LocationManager locManager = + (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); mHandler = new ProviderHandler(); // signal when we are initialized and ready to go mInitializedLatch.countDown(); + locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, + 0, 0, new NetworkLocationListener(), Looper.myLooper()); Looper.loop(); } } + private final class NetworkLocationListener implements LocationListener { + @Override + public void onLocationChanged(Location location) { + // this callback happens on mHandler looper + if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) { + handleUpdateLocation(location); + } + } + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { } + @Override + public void onProviderEnabled(String provider) { } + @Override + public void onProviderDisabled(String provider) { } + } + private String getSelectedApn() { Uri uri = Uri.parse("content://telephony/carriers/preferapn"); String apn = null; @@ -1647,6 +1593,22 @@ public class GpsLocationProvider implements LocationProviderInterface { return apn; } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + StringBuilder s = new StringBuilder(); + s.append(" mFixInterval=").append(mFixInterval).append("\n"); + s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" ("); + if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED "); + if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB "); + if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA "); + if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT "); + if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME "); + s.append(")\n"); + + s.append(native_get_internal_state()); + pw.append(s); + } + // for GPS SV statistics private static final int MAX_SVS = 32; private static final int EPHEMERIS_MASK = 0; diff --git a/services/java/com/android/server/location/LocationBasedCountryDetector.java b/services/java/com/android/server/location/LocationBasedCountryDetector.java index d4fb8ee..38871d7 100755 --- a/services/java/com/android/server/location/LocationBasedCountryDetector.java +++ b/services/java/com/android/server/location/LocationBasedCountryDetector.java @@ -114,7 +114,9 @@ public class LocationBasedCountryDetector extends CountryDetectorBase { for (String provider : providers) { Location lastKnownLocation = mLocationManager.getLastKnownLocation(provider); if (lastKnownLocation != null) { - if (bestLocation == null || bestLocation.getTime() < lastKnownLocation.getTime()) { + if (bestLocation == null || + bestLocation.getElapsedRealtimeNano() < + lastKnownLocation.getElapsedRealtimeNano()) { bestLocation = lastKnownLocation; } } diff --git a/services/java/com/android/server/location/LocationBlacklist.java b/services/java/com/android/server/location/LocationBlacklist.java new file mode 100644 index 0000000..71fa9f9 --- /dev/null +++ b/services/java/com/android/server/location/LocationBlacklist.java @@ -0,0 +1,135 @@ +/* + * 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.location; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings; +import android.util.Log; +import android.util.Slog; + +import com.android.server.LocationManagerService; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Allows applications to be blacklisted from location updates at run-time. + * + * This is a silent blacklist. Applications can still call Location Manager + * API's, but they just won't receive any locations. + */ +public final class LocationBlacklist extends ContentObserver { + private static final String TAG = "LocationBlacklist"; + private static final boolean D = LocationManagerService.D; + private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist"; + private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist"; + + private final Context mContext; + private final Object mLock = new Object(); + + // all fields below synchronized on mLock + private String[] mWhitelist = new String[0]; + private String[] mBlacklist = new String[0]; + + public LocationBlacklist(Context context, Handler handler) { + super(handler); + mContext = context; + } + + public void init() { + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + BLACKLIST_CONFIG_NAME), false, this); +// mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( +// WHITELIST_CONFIG_NAME), false, this); + reloadBlacklist(); + } + + 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)); + } + } + + /** + * Return true if in blacklist + * (package name matches blacklist, and does not match whitelist) + */ + public boolean isBlacklisted(String packageName) { + synchronized (mLock) { + for (String black : mBlacklist) { + if (packageName.startsWith(black)) { + if (inWhitelist(packageName)) { + continue; + } else { + if (D) Log.d(TAG, "dropping location (blacklisted): " + + packageName + " matches " + black); + return true; + } + } + } + } + return false; + } + + /** + * Return true if any of packages are in whitelist + */ + private boolean inWhitelist(String pkg) { + synchronized (mLock) { + for (String white : mWhitelist) { + if (pkg.startsWith(white)) return true; + } + } + return false; + } + + @Override + public void onChange(boolean selfChange) { + reloadBlacklist(); + } + + private String[] getStringArray(String key) { + String flatString = Settings.Secure.getString(mContext.getContentResolver(), key); + if (flatString == null) { + return new String[0]; + } + String[] splitStrings = flatString.split(","); + ArrayList<String> result = new ArrayList<String>(); + for (String pkg : splitStrings) { + pkg = pkg.trim(); + if (pkg.isEmpty()) { + continue; + } + result.add(pkg); + } + return result.toArray(new String[result.size()]); + } + + public void dump(PrintWriter pw) { + pw.println("mWhitelist=" + Arrays.toString(mWhitelist) + " mBlacklist=" + + Arrays.toString(mBlacklist)); + } +} diff --git a/services/java/com/android/server/location/LocationFudger.java b/services/java/com/android/server/location/LocationFudger.java new file mode 100644 index 0000000..57bc1c5 --- /dev/null +++ b/services/java/com/android/server/location/LocationFudger.java @@ -0,0 +1,310 @@ +/* + * 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.location; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.security.SecureRandom; +import android.location.Location; +import android.os.Bundle; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.Log; + + +/** + * Contains the logic to obfuscate (fudge) locations for coarse applications. + * + * <p>The goal is just to prevent applications with only + * the coarse location permission from receiving a fine location. + */ +public class LocationFudger { + private static final boolean D = false; + private static final String TAG = "LocationFudge"; + + private static final String EXTRA_COARSE_LOCATION = "coarseLocation"; + + /** + * This is the main control: Best location accuracy allowed for coarse applications. + */ + private static final float ACCURACY_METERS = 200.0f; + + /** + * The distance between grids for snap-to-grid. See {@link #createCoarse}. + */ + private static final double GRID_SIZE_METERS = ACCURACY_METERS; + + /** + * Standard deviation of the (normally distributed) random offset applied + * to coarse locations. It does not need to be as large as + * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation + * method. See further details in the implementation. + */ + private static final double STANDARD_DEVIATION_METERS = GRID_SIZE_METERS / 4.0; + + /** + * This is the fastest interval that applications can receive coarse + * locations. + */ + public static final long FASTEST_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes + + /** + * The duration until we change the random offset. + */ + private static final long CHANGE_INTERVAL_MS = 60 * 60 * 1000; // 1 hour + + /** + * The percentage that we change the random offset at every interval. + * + * <p>0.0 indicates the random offset doesn't change. 1.0 + * indicates the random offset is completely replaced every interval. + */ + private static final double CHANGE_PER_INTERVAL = 0.03; // 3% change + + // Pre-calculated weights used to move the random offset. + // + // The goal is to iterate on the previous offset, but keep + // the resulting standard deviation the same. The variance of + // two gaussian distributions summed together is equal to the + // sum of the variance of each distribution. So some quick + // algebra results in the following sqrt calculation to + // weigh in a new offset while keeping the final standard + // deviation unchanged. + private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL; + private static final double PREVIOUS_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT); + + /** + * This number actually varies because the earth is not round, but + * 111,000 meters is considered generally acceptable. + */ + private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000; + + /** + * Maximum latitude. + * + * <p>We pick a value 1 meter away from 90.0 degrees in order + * to keep cosine(MAX_LATITUDE) to a non-zero value, so that we avoid + * divide by zero fails. + */ + private static final double MAX_LATITUDE = 90.0 - + (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); + + private final Object mLock = new Object(); + private final SecureRandom mRandom = new SecureRandom(); + + // all fields below protected by mLock + private double mOffsetLatitudeMeters; + private double mOffsetLongitudeMeters; + private long mNextInterval; + + public LocationFudger() { + mOffsetLatitudeMeters = nextOffset(); + mOffsetLongitudeMeters = nextOffset(); + mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS; + } + + /** + * Get the cached coarse location, or generate a new one and cache it. + */ + public Location getOrCreate(Location location) { + Bundle extras = location.getExtras(); + if (extras == null) { + return addCoarseLocationExtra(location); + } + Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION); + if (parcel == null) { + return addCoarseLocationExtra(location); + } + if (!(parcel instanceof Location)) { + return addCoarseLocationExtra(location); + } + Location coarse = (Location) parcel; + if (coarse.getAccuracy() < ACCURACY_METERS) { + return addCoarseLocationExtra(location); + } + return coarse; + } + + private Location addCoarseLocationExtra(Location location) { + Bundle extras = location.getExtras(); + if (extras == null) extras = new Bundle(); + Location coarse = createCoarse(location); + extras.putParcelable(EXTRA_COARSE_LOCATION, coarse); + location.setExtras(extras); + return coarse; + } + + /** + * Create a coarse location. + * + * <p>Two techniques are used: random offsets and snap-to-grid. + * + * <p>First we add a random offset. This mitigates against detecting + * grid transitions. Without a random offset it is possible to detect + * a users position very accurately when they cross a grid boundary. + * The random offset changes very slowly over time, to mitigate against + * taking many location samples and averaging them out. + * + * <p>Second we snap-to-grid (quantize). This has the nice property of + * producing stable results, and mitigating against taking many samples + * to average out a random offset. + */ + private Location createCoarse(Location fine) { + Location coarse = new Location(fine); + + // clean all the optional information off the location, because + // this can leak detailed location information + coarse.removeBearing(); + coarse.removeSpeed(); + coarse.removeAltitude(); + coarse.setExtras(null); + + double lat = coarse.getLatitude(); + double lon = coarse.getLongitude(); + + // wrap + lat = wrapLatitude(lat); + lon = wrapLongitude(lon); + + // Step 1) apply a random offset + // + // The goal of the random offset is to prevent the application + // from determining that the device is on a grid boundary + // when it crosses from one grid to the next. + // + // We apply the offset even if the location already claims to be + // inaccurate, because it may be more accurate than claimed. + synchronized (mLock) { + updateRandomOffsetLocked(); + // perform lon first whilst lat is still within bounds + lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat); + lat += metersToDegreesLatitude(mOffsetLatitudeMeters); + if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)", + mOffsetLongitudeMeters, mOffsetLatitudeMeters)); + } + + // wrap + lat = wrapLatitude(lat); + lon = wrapLongitude(lon); + + // Step 2) Snap-to-grid (quantize) + // + // This is the primary means of obfuscation. It gives nice consistent + // results and is very effective at hiding the true location + // (as long as you are not sitting on a grid boundary, which + // step 1 mitigates). + // + // Note we quantize the latitude first, since the longitude + // quantization depends on the latitude value and so leaks information + // about the latitude + double latGranularity = metersToDegreesLatitude(GRID_SIZE_METERS); + lat = Math.round(lat / latGranularity) * latGranularity; + double lonGranularity = metersToDegreesLongitude(GRID_SIZE_METERS, lat); + lon = Math.round(lon / lonGranularity) * lonGranularity; + + // wrap again + lat = wrapLatitude(lat); + lon = wrapLongitude(lon); + + // apply + coarse.setLatitude(lat); + coarse.setLongitude(lon); + coarse.setAccuracy(Math.max(ACCURACY_METERS, coarse.getAccuracy())); + + if (D) Log.d(TAG, "fudged " + fine + " to " + coarse); + return coarse; + } + + /** + * Update the random offset over time. + * + * <p>If the random offset was new for every location + * fix then an application can more easily average location results + * over time, + * especially when the location is near a grid boundary. On the + * other hand if the random offset is constant then if an application + * found a way to reverse engineer the offset they would be able + * to detect location at grid boundaries very accurately. So + * we choose a random offset and then very slowly move it, to + * make both approaches very hard. + * + * <p>The random offset does not need to be large, because snap-to-grid + * is the primary obfuscation mechanism. It just needs to be large + * enough to stop information leakage as we cross grid boundaries. + */ + private void updateRandomOffsetLocked() { + long now = SystemClock.elapsedRealtime(); + if (now < mNextInterval) { + return; + } + + if (D) Log.d(TAG, String.format("old offset: %.0f, %.0f (meters)", + mOffsetLongitudeMeters, mOffsetLatitudeMeters)); + + // ok, need to update the random offset + mNextInterval = now + CHANGE_INTERVAL_MS; + + mOffsetLatitudeMeters *= PREVIOUS_WEIGHT; + mOffsetLatitudeMeters += NEW_WEIGHT * nextOffset(); + mOffsetLongitudeMeters *= PREVIOUS_WEIGHT; + mOffsetLongitudeMeters += NEW_WEIGHT * nextOffset(); + + if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)", + mOffsetLongitudeMeters, mOffsetLatitudeMeters)); + } + + private double nextOffset() { + return mRandom.nextGaussian() * STANDARD_DEVIATION_METERS; + } + + private static double wrapLatitude(double lat) { + if (lat > MAX_LATITUDE) { + lat = MAX_LATITUDE; + } + if (lat < -MAX_LATITUDE) { + lat = -MAX_LATITUDE; + } + return lat; + } + + private static double wrapLongitude(double lon) { + lon %= 360.0; // wraps into range (-360.0, +360.0) + if (lon >= 180.0) { + lon -= 360.0; + } + if (lon < -180.0) { + lon += 360.0; + } + return lon; + } + + private static double metersToDegreesLatitude(double distance) { + return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR; + } + + /** + * Requires latitude since longitudinal distances change with distance from equator. + */ + private static double metersToDegreesLongitude(double distance, double lat) { + return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat)); + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters, + mOffsetLatitudeMeters)); + } +} diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java index 858a582..6f09232 100644 --- a/services/java/com/android/server/location/LocationProviderInterface.java +++ b/services/java/com/android/server/location/LocationProviderInterface.java @@ -16,42 +16,33 @@ package com.android.server.location; -import android.location.Criteria; -import android.location.Location; -import android.net.NetworkInfo; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; + + import android.os.Bundle; import android.os.WorkSource; /** * Location Manager's interface for location providers. - * - * {@hide} + * @hide */ public interface LocationProviderInterface { - String getName(); - boolean requiresNetwork(); - boolean requiresSatellite(); - boolean requiresCell(); - boolean hasMonetaryCost(); - boolean supportsAltitude(); - boolean supportsSpeed(); - boolean supportsBearing(); - int getPowerRequirement(); - boolean meetsCriteria(Criteria criteria); - int getAccuracy(); - boolean isEnabled(); - void enable(); - void disable(); - int getStatus(Bundle extras); - long getStatusUpdateTime(); - void enableLocationTracking(boolean enable); - /* returns false if single shot is not supported */ - boolean requestSingleShotFix(); - String getInternalState(); - void setMinTime(long minTime, WorkSource ws); - void updateNetworkState(int state, NetworkInfo info); - void updateLocation(Location location); - boolean sendExtraCommand(String command, Bundle extras); - void addListener(int uid); - void removeListener(int uid); + public String getName(); + + public void enable(); + public void disable(); + public boolean isEnabled(); + public void setRequest(ProviderRequest request, WorkSource source); + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args); + + // --- deprecated (but still supported) --- + public ProviderProperties getProperties(); + public int getStatus(Bundle extras); + public long getStatusUpdateTime(); + public boolean sendExtraCommand(String command, Bundle extras); } diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java index a227ab6..7faf72c 100644 --- a/services/java/com/android/server/location/LocationProviderProxy.java +++ b/services/java/com/android/server/location/LocationProviderProxy.java @@ -16,458 +16,272 @@ package com.android.server.location; -import android.content.ComponentName; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.location.Criteria; -import android.location.ILocationProvider; -import android.location.Location; -import android.net.NetworkInfo; +import android.location.LocationProvider; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.os.RemoteException; import android.os.WorkSource; import android.util.Log; -import com.android.internal.location.DummyLocationProvider; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ILocationProvider; +import com.android.internal.location.ProviderRequest; +import com.android.server.LocationManagerService; +import com.android.server.ServiceWatcher; /** - * A class for proxying location providers implemented as services. - * - * {@hide} + * Proxy for ILocationProvider implementations. */ public class LocationProviderProxy implements LocationProviderInterface { - private static final String TAG = "LocationProviderProxy"; - - public static final String SERVICE_ACTION = - "com.android.location.service.NetworkLocationProvider"; + private static final boolean D = LocationManagerService.D; private final Context mContext; private final String mName; - private final Intent mIntent; - private final Handler mHandler; - private final Object mMutex = new Object(); // synchronizes access to non-final members - private Connection mServiceConnection; // never null after ctor + private final ServiceWatcher mServiceWatcher; + + private Object mLock = new Object(); - // cached values set by the location manager - private boolean mLocationTracking = false; + // cached values set by the location manager, synchronized on mLock + private ProviderProperties mProperties; private boolean mEnabled = false; - private long mMinTime = -1; - private WorkSource mMinTimeSource = new WorkSource(); - private int mNetworkState; - private NetworkInfo mNetworkInfo; - - // constructor for proxying location providers implemented in a separate service - public LocationProviderProxy(Context context, String name, String packageName, - Handler handler) { + private ProviderRequest mRequest = null; + private WorkSource mWorksource = new WorkSource(); + + public static LocationProviderProxy createAndBind(Context context, String name, String action, + List<String> initialPackageNames, Handler handler) { + LocationProviderProxy proxy = new LocationProviderProxy(context, name, action, + initialPackageNames, handler); + if (proxy.bind()) { + return proxy; + } else { + return null; + } + } + + private LocationProviderProxy(Context context, String name, String action, + List<String> initialPackageNames, Handler handler) { mContext = context; mName = name; - mIntent = new Intent(SERVICE_ACTION); - mHandler = handler; - reconnect(packageName); + mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames, + mNewServiceWork, handler); } - /** Bind to service. Will reconnect if already connected */ - public void reconnect(String packageName) { - synchronized (mMutex) { - if (mServiceConnection != null) { - mContext.unbindService(mServiceConnection); - } - mServiceConnection = new Connection(); - mIntent.setPackage(packageName); - mContext.bindService(mIntent, mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | - Context.BIND_ALLOW_OOM_MANAGEMENT); - } + private boolean bind () { + return mServiceWatcher.start(); } - private class Connection implements ServiceConnection, Runnable { - - private ILocationProvider mProvider; - - // for caching requiresNetwork, requiresSatellite, etc. - private DummyLocationProvider mCachedAttributes; // synchronized by mMutex + private ILocationProvider getService() { + return ILocationProvider.Stub.asInterface(mServiceWatcher.getBinder()); + } - public void onServiceConnected(ComponentName className, IBinder service) { - synchronized (this) { - mProvider = ILocationProvider.Stub.asInterface(service); - if (mProvider != null) { - mHandler.post(this); - } - } - } + public String getConnectedPackageName() { + return mServiceWatcher.getBestPackageName(); + } - public void onServiceDisconnected(ComponentName className) { - synchronized (this) { - mProvider = null; + /** + * Work to apply current state to a newly connected provider. + * Remember we can switch the service that implements a providers + * at run-time, so need to apply current state. + */ + private Runnable mNewServiceWork = new Runnable() { + @Override + public void run() { + if (D) Log.d(TAG, "applying state to connected service"); + + boolean enabled; + ProviderProperties properties = null; + ProviderRequest request; + WorkSource source; + ILocationProvider service; + synchronized (mLock) { + enabled = mEnabled; + request = mRequest; + source = mWorksource; + service = getService(); } - } - public synchronized ILocationProvider getProvider() { - return mProvider; - } - - public synchronized DummyLocationProvider getCachedAttributes() { - return mCachedAttributes; - } + if (service == null) return; - public void run() { - synchronized (mMutex) { - if (mServiceConnection != this) { - // This ServiceConnection no longer the one we want to bind to. - return; - } - ILocationProvider provider = getProvider(); - if (provider == null) { - return; + try { + // load properties from provider + properties = service.getProperties(); + if (properties == null) { + Log.e(TAG, mServiceWatcher.getBestPackageName() + + " has invalid locatino provider properties"); } - // resend previous values from the location manager if the service has restarted - try { - if (mEnabled) { - provider.enable(); - } - if (mLocationTracking) { - provider.enableLocationTracking(true); + // apply current state to new service + if (enabled) { + service.enable(); + if (request != null) { + service.setRequest(request, source); } - if (mMinTime >= 0) { - provider.setMinTime(mMinTime, mMinTimeSource); - } - if (mNetworkInfo != null) { - provider.updateNetworkState(mNetworkState, mNetworkInfo); - } - } catch (RemoteException e) { } + } catch (RemoteException e) { + Log.w(TAG, e); + } catch (Exception e) { + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); + } - // init cache of parameters - if (mCachedAttributes == null) { - try { - mCachedAttributes = new DummyLocationProvider(mName, null); - mCachedAttributes.setRequiresNetwork(provider.requiresNetwork()); - mCachedAttributes.setRequiresSatellite(provider.requiresSatellite()); - mCachedAttributes.setRequiresCell(provider.requiresCell()); - mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost()); - mCachedAttributes.setSupportsAltitude(provider.supportsAltitude()); - mCachedAttributes.setSupportsSpeed(provider.supportsSpeed()); - mCachedAttributes.setSupportsBearing(provider.supportsBearing()); - mCachedAttributes.setPowerRequirement(provider.getPowerRequirement()); - mCachedAttributes.setAccuracy(provider.getAccuracy()); - } catch (RemoteException e) { - mCachedAttributes = null; - } - } + synchronized (mLock) { + mProperties = properties; } } }; + @Override public String getName() { return mName; } - private DummyLocationProvider getCachedAttributes() { - synchronized (mMutex) { - return mServiceConnection.getCachedAttributes(); - } - } - - public boolean requiresNetwork() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.requiresNetwork(); - } else { - return false; - } - } - - public boolean requiresSatellite() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.requiresSatellite(); - } else { - return false; - } - } - - public boolean requiresCell() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.requiresCell(); - } else { - return false; - } - } - - public boolean hasMonetaryCost() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.hasMonetaryCost(); - } else { - return false; - } - } - - public boolean supportsAltitude() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.supportsAltitude(); - } else { - return false; - } - } - - public boolean supportsSpeed() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.supportsSpeed(); - } else { - return false; - } - } - - public boolean supportsBearing() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.supportsBearing(); - } else { - return false; - } - } - - public int getPowerRequirement() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.getPowerRequirement(); - } else { - return -1; + @Override + public ProviderProperties getProperties() { + synchronized (mLock) { + return mProperties; } } - public int getAccuracy() { - DummyLocationProvider cachedAttributes = getCachedAttributes(); - if (cachedAttributes != null) { - return cachedAttributes.getAccuracy(); - } else { - return -1; - } - } - - public boolean meetsCriteria(Criteria criteria) { - synchronized (mMutex) { - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - return provider.meetsCriteria(criteria); - } catch (RemoteException e) { - } - } - } - // default implementation if we lost connection to the provider - if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) && - (criteria.getAccuracy() < getAccuracy())) { - return false; - } - int criteriaPower = criteria.getPowerRequirement(); - if ((criteriaPower != Criteria.NO_REQUIREMENT) && - (criteriaPower < getPowerRequirement())) { - return false; - } - if (criteria.isAltitudeRequired() && !supportsAltitude()) { - return false; - } - if (criteria.isSpeedRequired() && !supportsSpeed()) { - return false; - } - if (criteria.isBearingRequired() && !supportsBearing()) { - return false; - } - return true; - } - + @Override public void enable() { - synchronized (mMutex) { + synchronized (mLock) { mEnabled = true; - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.enable(); - } catch (RemoteException e) { - } - } } - } + ILocationProvider service = getService(); + if (service == null) return; - public void disable() { - synchronized (mMutex) { - mEnabled = false; - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.disable(); - } catch (RemoteException e) { - } - } + try { + service.enable(); + } catch (RemoteException e) { + Log.w(TAG, e); + } catch (Exception e) { + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); } } - public boolean isEnabled() { - synchronized (mMutex) { - return mEnabled; + @Override + public void disable() { + synchronized (mLock) { + mEnabled = false; } - } + ILocationProvider service = getService(); + if (service == null) return; - public int getStatus(Bundle extras) { - ILocationProvider provider; - synchronized (mMutex) { - provider = mServiceConnection.getProvider(); + try { + service.disable(); + } catch (RemoteException e) { + Log.w(TAG, e); + } catch (Exception e) { + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); } - if (provider != null) { - try { - return provider.getStatus(extras); - } catch (RemoteException e) { - } - } - return 0; } - public long getStatusUpdateTime() { - ILocationProvider provider; - synchronized (mMutex) { - provider = mServiceConnection.getProvider(); - } - if (provider != null) { - try { - return provider.getStatusUpdateTime(); - } catch (RemoteException e) { - } - } - return 0; - } - - public String getInternalState() { - ILocationProvider provider; - synchronized (mMutex) { - provider = mServiceConnection.getProvider(); - } - if (provider != null) { - try { - return provider.getInternalState(); - } catch (RemoteException e) { - Log.e(TAG, "getInternalState failed", e); - } + @Override + public boolean isEnabled() { + synchronized (mLock) { + return mEnabled; } - return null; } - public boolean isLocationTracking() { - synchronized (mMutex) { - return mLocationTracking; + @Override + public void setRequest(ProviderRequest request, WorkSource source) { + synchronized (mLock) { + mRequest = request; + mWorksource = source; } - } + ILocationProvider service = getService(); + if (service == null) return; - public void enableLocationTracking(boolean enable) { - synchronized (mMutex) { - mLocationTracking = enable; - if (!enable) { - mMinTime = -1; - mMinTimeSource.clear(); - } - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.enableLocationTracking(enable); - } catch (RemoteException e) { - } - } + try { + service.setRequest(request, source); + } catch (RemoteException e) { + Log.w(TAG, e); + } catch (Exception e) { + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); } } - public boolean requestSingleShotFix() { - return false; - } - - public long getMinTime() { - synchronized (mMutex) { - return mMinTime; + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.append("REMOTE SERVICE"); + pw.append(" name=").append(mName); + pw.append(" pkg=").append(mServiceWatcher.getBestPackageName()); + pw.append(" version=").append("" + mServiceWatcher.getBestVersion()); + pw.append('\n'); + + ILocationProvider service = getService(); + if (service == null) { + pw.println("service down (null)"); + return; + } + pw.flush(); + + try { + service.asBinder().dump(fd, args); + } catch (RemoteException e) { + pw.println("service down (RemoteException)"); + Log.w(TAG, e); + } catch (Exception e) { + pw.println("service down (Exception)"); + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); } } - public void setMinTime(long minTime, WorkSource ws) { - synchronized (mMutex) { - mMinTime = minTime; - mMinTimeSource.set(ws); - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.setMinTime(minTime, ws); - } catch (RemoteException e) { - } - } - } + @Override + public int getStatus(Bundle extras) { + ILocationProvider service = getService(); + if (service == null) return LocationProvider.TEMPORARILY_UNAVAILABLE; + + try { + return service.getStatus(extras); + } catch (RemoteException e) { + Log.w(TAG, e); + } catch (Exception e) { + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); + } + return LocationProvider.TEMPORARILY_UNAVAILABLE; } - public void updateNetworkState(int state, NetworkInfo info) { - synchronized (mMutex) { - mNetworkState = state; - mNetworkInfo = info; - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.updateNetworkState(state, info); - } catch (RemoteException e) { - } - } - } - } + @Override + public long getStatusUpdateTime() { + ILocationProvider service = getService(); + if (service == null) return 0; - public void updateLocation(Location location) { - synchronized (mMutex) { - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.updateLocation(location); - } catch (RemoteException e) { - } - } + try { + return service.getStatusUpdateTime(); + } catch (RemoteException e) { + Log.w(TAG, e); + } catch (Exception e) { + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); } + return 0; } + @Override public boolean sendExtraCommand(String command, Bundle extras) { - synchronized (mMutex) { - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - return provider.sendExtraCommand(command, extras); - } catch (RemoteException e) { - } - } - } - return false; - } + ILocationProvider service = getService(); + if (service == null) return false; - public void addListener(int uid) { - synchronized (mMutex) { - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.addListener(uid); - } catch (RemoteException e) { - } - } - } - } - - public void removeListener(int uid) { - synchronized (mMutex) { - ILocationProvider provider = mServiceConnection.getProvider(); - if (provider != null) { - try { - provider.removeListener(uid); - } catch (RemoteException e) { - } - } + try { + return service.sendExtraCommand(command, extras); + } catch (RemoteException e) { + Log.w(TAG, e); + } catch (Exception e) { + // never let remote service crash system server + Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e); } + return false; } -} + } diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java index 09d799f..36c43ff 100644 --- a/services/java/com/android/server/location/MockProvider.java +++ b/services/java/com/android/server/location/MockProvider.java @@ -20,15 +20,19 @@ import android.location.Criteria; import android.location.ILocationManager; import android.location.Location; import android.location.LocationProvider; -import android.net.NetworkInfo; import android.os.Bundle; import android.os.RemoteException; import android.os.WorkSource; import android.util.Log; import android.util.PrintWriterPrinter; + +import java.io.FileDescriptor; import java.io.PrintWriter; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; + /** * A mock location provider used by LocationManagerService to implement test providers. * @@ -36,60 +40,56 @@ import java.io.PrintWriter; */ public class MockProvider implements LocationProviderInterface { private final String mName; + private final ProviderProperties mProperties; private final ILocationManager mLocationManager; - private final boolean mRequiresNetwork; - private final boolean mRequiresSatellite; - private final boolean mRequiresCell; - private final boolean mHasMonetaryCost; - private final boolean mSupportsAltitude; - private final boolean mSupportsSpeed; - private final boolean mSupportsBearing; - private final int mPowerRequirement; - private final int mAccuracy; + private final Location mLocation; + private final Bundle mExtras = new Bundle(); + private int mStatus; private long mStatusUpdateTime; - private final Bundle mExtras = new Bundle(); private boolean mHasLocation; private boolean mHasStatus; private boolean mEnabled; private static final String TAG = "MockProvider"; - public MockProvider(String name, ILocationManager locationManager, - boolean requiresNetwork, boolean requiresSatellite, - boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, - boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { + public MockProvider(String name, ILocationManager locationManager, + ProviderProperties properties) { + if (properties == null) throw new NullPointerException("properties is null"); + mName = name; mLocationManager = locationManager; - mRequiresNetwork = requiresNetwork; - mRequiresSatellite = requiresSatellite; - mRequiresCell = requiresCell; - mHasMonetaryCost = hasMonetaryCost; - mSupportsAltitude = supportsAltitude; - mSupportsBearing = supportsBearing; - mSupportsSpeed = supportsSpeed; - mPowerRequirement = powerRequirement; - mAccuracy = accuracy; + mProperties = properties; mLocation = new Location(name); } + @Override public String getName() { return mName; } + @Override + public ProviderProperties getProperties() { + return mProperties; + } + + @Override public void disable() { mEnabled = false; } + @Override public void enable() { mEnabled = true; } + @Override public boolean isEnabled() { return mEnabled; } + @Override public int getStatus(Bundle extras) { if (mHasStatus) { extras.clear(); @@ -100,75 +100,20 @@ public class MockProvider implements LocationProviderInterface { } } + @Override public long getStatusUpdateTime() { return mStatusUpdateTime; } - public int getAccuracy() { - return mAccuracy; - } - - public int getPowerRequirement() { - return mPowerRequirement; - } - - public boolean hasMonetaryCost() { - return mHasMonetaryCost; - } - - public boolean requiresCell() { - return mRequiresCell; - } - - public boolean requiresNetwork() { - return mRequiresNetwork; - } - - public boolean requiresSatellite() { - return mRequiresSatellite; - } - - public boolean supportsAltitude() { - return mSupportsAltitude; - } - - public boolean supportsBearing() { - return mSupportsBearing; - } - - public boolean supportsSpeed() { - return mSupportsSpeed; - } - - public boolean meetsCriteria(Criteria criteria) { - if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) && - (criteria.getAccuracy() < mAccuracy)) { - return false; - } - int criteriaPower = criteria.getPowerRequirement(); - if ((criteriaPower != Criteria.NO_REQUIREMENT) && - (criteriaPower < mPowerRequirement)) { - return false; - } - if (criteria.isAltitudeRequired() && !mSupportsAltitude) { - return false; - } - if (criteria.isSpeedRequired() && !mSupportsSpeed) { - return false; - } - if (criteria.isBearingRequired() && !mSupportsBearing) { - return false; - } - return true; - } - public void setLocation(Location l) { mLocation.set(l); mHasLocation = true; - try { - mLocationManager.reportLocation(mLocation, false); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling reportLocation"); + if (mEnabled) { + try { + mLocationManager.reportLocation(mLocation, false); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException calling reportLocation"); + } } } @@ -191,34 +136,9 @@ public class MockProvider implements LocationProviderInterface { mStatusUpdateTime = 0; } - public String getInternalState() { - return null; - } - - public void enableLocationTracking(boolean enable) { - } - - public boolean requestSingleShotFix() { - return false; - } - - public void setMinTime(long minTime, WorkSource ws) { - } - - public void updateNetworkState(int state, NetworkInfo info) { - } - - public void updateLocation(Location location) { - } - - public boolean sendExtraCommand(String command, Bundle extras) { - return false; - } - - public void addListener(int uid) { - } - - public void removeListener(int uid) { + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + dump(pw, ""); } public void dump(PrintWriter pw, String prefix) { @@ -231,4 +151,12 @@ public class MockProvider implements LocationProviderInterface { pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime); pw.println(prefix + "mExtras=" + mExtras); } + + @Override + public void setRequest(ProviderRequest request, WorkSource source) { } + + @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 ea0d1b0..0ce21b7 100644 --- a/services/java/com/android/server/location/PassiveProvider.java +++ b/services/java/com/android/server/location/PassiveProvider.java @@ -16,17 +16,23 @@ package com.android.server.location; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; + import android.location.Criteria; import android.location.ILocationManager; import android.location.Location; import android.location.LocationManager; import android.location.LocationProvider; -import android.net.NetworkInfo; import android.os.Bundle; import android.os.RemoteException; import android.os.WorkSource; import android.util.Log; + /** * A passive location provider reports locations received from other providers * for clients that want to listen passively without actually triggering @@ -35,103 +41,63 @@ import android.util.Log; * {@hide} */ public class PassiveProvider implements LocationProviderInterface { - private static final String TAG = "PassiveProvider"; + private static final ProviderProperties PROPERTIES = new ProviderProperties( + false, false, false, false, false, false, false, + Criteria.POWER_LOW, Criteria.ACCURACY_COARSE); + private final ILocationManager mLocationManager; - private boolean mTracking; + private boolean mReportLocation; public PassiveProvider(ILocationManager locationManager) { mLocationManager = locationManager; } + @Override public String getName() { return LocationManager.PASSIVE_PROVIDER; } - public boolean requiresNetwork() { - return false; - } - - public boolean requiresSatellite() { - return false; - } - - public boolean requiresCell() { - return false; - } - - public boolean hasMonetaryCost() { - return false; - } - - public boolean supportsAltitude() { - return false; - } - - public boolean supportsSpeed() { - return false; - } - - public boolean supportsBearing() { - return false; - } - - public int getPowerRequirement() { - return -1; - } - - public boolean meetsCriteria(Criteria criteria) { - // We do not want to match the special passive provider based on criteria. - return false; - } - - public int getAccuracy() { - return -1; + @Override + public ProviderProperties getProperties() { + return PROPERTIES; } + @Override public boolean isEnabled() { return true; } + @Override public void enable() { } + @Override public void disable() { } + @Override public int getStatus(Bundle extras) { - if (mTracking) { + if (mReportLocation) { return LocationProvider.AVAILABLE; } else { return LocationProvider.TEMPORARILY_UNAVAILABLE; } } + @Override public long getStatusUpdateTime() { return -1; } - public String getInternalState() { - return null; - } - - public void enableLocationTracking(boolean enable) { - mTracking = enable; - } - - public boolean requestSingleShotFix() { - return false; - } - - public void setMinTime(long minTime, WorkSource ws) { - } - - public void updateNetworkState(int state, NetworkInfo info) { + @Override + public void setRequest(ProviderRequest request, WorkSource source) { + mReportLocation = request.reportLocation; } public void updateLocation(Location location) { - if (mTracking) { + if (mReportLocation) { try { // pass the location back to the location manager mLocationManager.reportLocation(location, true); @@ -141,13 +107,13 @@ public class PassiveProvider implements LocationProviderInterface { } } + @Override public boolean sendExtraCommand(String command, Bundle extras) { return false; } - public void addListener(int uid) { - } - - public void removeListener(int uid) { + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mReportLocaiton=" + mReportLocation); } } diff --git a/services/java/com/android/server/net/NetworkAlertObserver.java b/services/java/com/android/server/net/BaseNetworkObserver.java index 0d1c3b2..8b2aa5d 100644 --- a/services/java/com/android/server/net/NetworkAlertObserver.java +++ b/services/java/com/android/server/net/BaseNetworkObserver.java @@ -19,26 +19,39 @@ package com.android.server.net; import android.net.INetworkManagementEventObserver; /** + * Base {@link INetworkManagementEventObserver} that provides no-op + * implementations which can be overridden. + * * @hide */ -public abstract class NetworkAlertObserver extends INetworkManagementEventObserver.Stub { +public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub { @Override public void interfaceStatusChanged(String iface, boolean up) { - // ignored; interface changes come through ConnectivityService + // default no-op } @Override public void interfaceRemoved(String iface) { - // ignored; interface changes come through ConnectivityService + // default no-op } @Override public void interfaceLinkStateChanged(String iface, boolean up) { - // ignored; interface changes come through ConnectivityService + // default no-op } @Override public void interfaceAdded(String iface) { - // ignored; interface changes come through ConnectivityService + // default no-op + } + + @Override + public void interfaceClassDataActivityChanged(String label, boolean active) { + // default no-op + } + + @Override + public void limitReached(String limitName, String iface) { + // default no-op } } diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java new file mode 100644 index 0000000..541650e --- /dev/null +++ b/services/java/com/android/server/net/LockdownVpnTracker.java @@ -0,0 +1,271 @@ +/* + * 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.net; + +import static android.Manifest.permission.CONNECTIVITY_INTERNAL; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.LinkProperties; +import android.net.NetworkInfo; +import android.net.NetworkInfo.DetailedState; +import android.net.NetworkInfo.State; +import android.os.INetworkManagementService; +import android.os.RemoteException; +import android.security.Credentials; +import android.security.KeyStore; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; +import com.android.internal.util.Preconditions; +import com.android.server.ConnectivityService; +import com.android.server.connectivity.Vpn; + +/** + * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be + * connected and kicks off VPN connection, managing any required {@code netd} + * firewall rules. + */ +public class LockdownVpnTracker { + private static final String TAG = "LockdownVpnTracker"; + + /** Number of VPN attempts before waiting for user intervention. */ + private static final int MAX_ERROR_COUNT = 4; + + private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET"; + + private final Context mContext; + private final INetworkManagementService mNetService; + private final ConnectivityService mConnService; + private final Vpn mVpn; + private final VpnProfile mProfile; + + private final Object mStateLock = new Object(); + + private PendingIntent mResetIntent; + + private String mAcceptedEgressIface; + private String mAcceptedIface; + private String mAcceptedSourceAddr; + + private int mErrorCount; + + public static boolean isEnabled() { + return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN); + } + + public LockdownVpnTracker(Context context, INetworkManagementService netService, + ConnectivityService connService, Vpn vpn, VpnProfile profile) { + mContext = Preconditions.checkNotNull(context); + mNetService = Preconditions.checkNotNull(netService); + mConnService = Preconditions.checkNotNull(connService); + mVpn = Preconditions.checkNotNull(vpn); + mProfile = Preconditions.checkNotNull(profile); + + final Intent intent = new Intent(ACTION_LOCKDOWN_RESET); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mResetIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } + + private BroadcastReceiver mResetReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + reset(); + } + }; + + /** + * Watch for state changes to both active egress network, kicking off a VPN + * connection when ready, or setting firewall rules once VPN is connected. + */ + private void handleStateChangedLocked() { + Slog.d(TAG, "handleStateChanged()"); + + final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered(); + final LinkProperties egressProp = mConnService.getActiveLinkProperties(); + + final NetworkInfo vpnInfo = mVpn.getNetworkInfo(); + final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig(); + + // Restart VPN when egress network disconnected or changed + final boolean egressDisconnected = egressInfo == null + || State.DISCONNECTED.equals(egressInfo.getState()); + final boolean egressChanged = egressProp == null + || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName()); + if (egressDisconnected || egressChanged) { + clearSourceRules(); + mAcceptedEgressIface = null; + mVpn.stopLegacyVpn(); + } + if (egressDisconnected) return; + + if (mErrorCount > MAX_ERROR_COUNT) { + showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected); + + } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) { + if (mProfile.isValidLockdownProfile()) { + Slog.d(TAG, "Active network connected; starting VPN"); + showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected); + + mAcceptedEgressIface = egressProp.getInterfaceName(); + mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp); + + } else { + Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS"); + showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected); + } + + } else if (vpnInfo.isConnected() && vpnConfig != null) { + final String iface = vpnConfig.interfaze; + final String sourceAddr = vpnConfig.addresses; + + if (TextUtils.equals(iface, mAcceptedIface) + && TextUtils.equals(sourceAddr, mAcceptedSourceAddr)) { + return; + } + + Slog.d(TAG, "VPN connected using iface=" + iface + ", sourceAddr=" + sourceAddr); + showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected); + + try { + clearSourceRules(); + + mNetService.setFirewallInterfaceRule(iface, true); + mNetService.setFirewallEgressSourceRule(sourceAddr, true); + + mErrorCount = 0; + mAcceptedIface = iface; + mAcceptedSourceAddr = sourceAddr; + } catch (RemoteException e) { + throw new RuntimeException("Problem setting firewall rules", e); + } + + mConnService.sendConnectedBroadcast(augmentNetworkInfo(egressInfo)); + } + } + + public void init() { + Slog.d(TAG, "init()"); + + mVpn.setEnableNotifications(false); + + final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET); + mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null); + + try { + // TODO: support non-standard port numbers + mNetService.setFirewallEgressDestRule(mProfile.server, 500, true); + mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true); + } catch (RemoteException e) { + throw new RuntimeException("Problem setting firewall rules", e); + } + + synchronized (mStateLock) { + handleStateChangedLocked(); + } + } + + public void shutdown() { + Slog.d(TAG, "shutdown()"); + + mAcceptedEgressIface = null; + mErrorCount = 0; + + mVpn.stopLegacyVpn(); + try { + mNetService.setFirewallEgressDestRule(mProfile.server, 500, false); + mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false); + } catch (RemoteException e) { + throw new RuntimeException("Problem setting firewall rules", e); + } + clearSourceRules(); + hideNotification(); + + mContext.unregisterReceiver(mResetReceiver); + mVpn.setEnableNotifications(true); + } + + public void reset() { + // cycle tracker, reset error count, and trigger retry + shutdown(); + init(); + synchronized (mStateLock) { + handleStateChangedLocked(); + } + } + + private void clearSourceRules() { + try { + if (mAcceptedIface != null) { + mNetService.setFirewallInterfaceRule(mAcceptedIface, false); + mAcceptedIface = null; + } + if (mAcceptedSourceAddr != null) { + mNetService.setFirewallEgressSourceRule(mAcceptedSourceAddr, false); + mAcceptedSourceAddr = null; + } + } catch (RemoteException e) { + throw new RuntimeException("Problem setting firewall rules", e); + } + } + + public void onNetworkInfoChanged(NetworkInfo info) { + synchronized (mStateLock) { + handleStateChangedLocked(); + } + } + + public void onVpnStateChanged(NetworkInfo info) { + if (info.getDetailedState() == DetailedState.FAILED) { + mErrorCount++; + } + synchronized (mStateLock) { + handleStateChangedLocked(); + } + } + + public NetworkInfo augmentNetworkInfo(NetworkInfo info) { + final NetworkInfo vpnInfo = mVpn.getNetworkInfo(); + info = new NetworkInfo(info); + info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null); + return info; + } + + private void showNotification(int titleRes, int iconRes) { + final Notification.Builder builder = new Notification.Builder(mContext); + builder.setWhen(0); + builder.setSmallIcon(iconRes); + builder.setContentTitle(mContext.getString(titleRes)); + builder.setContentText(mContext.getString(R.string.vpn_lockdown_reset)); + builder.setContentIntent(mResetIntent); + builder.setPriority(Notification.PRIORITY_LOW); + builder.setOngoing(true); + NotificationManager.from(mContext).notify(TAG, 0, builder.build()); + } + + private void hideNotification() { + NotificationManager.from(mContext).cancel(TAG, 0); + } +} diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index fe43d11..a7cba5a 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -112,11 +112,13 @@ import android.os.Message; import android.os.MessageQueue.IdleHandler; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.UserId; +import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.format.Formatter; import android.text.format.Time; +import android.util.AtomicFile; import android.util.Log; import android.util.NtpTrustedTime; import android.util.Slog; @@ -127,7 +129,6 @@ import android.util.TrustedTime; import android.util.Xml; import com.android.internal.R; -import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Objects; @@ -425,7 +426,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final String action = intent.getAction(); final int uid = intent.getIntExtra(EXTRA_UID, 0); - final int appId = UserId.getAppId(uid); + final int appId = UserHandle.getAppId(uid); synchronized (mRulesLock) { if (ACTION_PACKAGE_ADDED.equals(action)) { // NOTE: PACKAGE_ADDED is currently only sent once, and is @@ -570,7 +571,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * Observer that watches for {@link INetworkManagementService} alerts. */ - private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() { + private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() { @Override public void limitReached(String limitName, String iface) { // only someone like NMS should be calling us @@ -1187,8 +1188,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int uid = readIntAttribute(in, ATTR_UID); final int policy = readIntAttribute(in, ATTR_POLICY); - final int appId = UserId.getAppId(uid); - if (UserId.isApp(appId)) { + final int appId = UserHandle.getAppId(uid); + if (UserHandle.isApp(appId)) { setAppPolicyUnchecked(appId, policy, false); } else { Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring"); @@ -1197,7 +1198,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int appId = readIntAttribute(in, ATTR_APP_ID); final int policy = readIntAttribute(in, ATTR_POLICY); - if (UserId.isApp(appId)) { + if (UserHandle.isApp(appId)) { setAppPolicyUnchecked(appId, policy, false); } else { Slog.w(TAG, "unable to apply policy to appId " + appId + "; ignoring"); @@ -1303,7 +1304,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void setAppPolicy(int appId, int policy) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - if (!UserId.isApp(appId)) { + if (!UserHandle.isApp(appId)) { throw new IllegalArgumentException("cannot apply policy to appId " + appId); } @@ -1697,7 +1698,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final PackageManager pm = mContext.getPackageManager(); final List<ApplicationInfo> apps = pm.getInstalledApplications(0); for (ApplicationInfo app : apps) { - final int appId = UserId.getAppId(app.uid); + final int appId = UserHandle.getAppId(app.uid); updateRulesForAppLocked(appId); } @@ -1707,8 +1708,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } private void updateRulesForAppLocked(int appId) { - for (UserInfo user : mContext.getPackageManager().getUsers()) { - final int uid = UserId.getUid(user.id, appId); + UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + for (UserInfo user : um.getUsers()) { + final int uid = UserHandle.getUid(user.id, appId); updateRulesForUidLocked(uid); } } @@ -1716,7 +1718,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static boolean isUidValidForRules(int uid) { // allow rules on specific system services, and any apps if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID - || UserId.isApp(uid)) { + || UserHandle.isApp(uid)) { return true; } @@ -1726,7 +1728,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void updateRulesForUidLocked(int uid) { if (!isUidValidForRules(uid)) return; - final int appId = UserId.getAppId(uid); + final int appId = UserHandle.getAppId(uid); final int appPolicy = getAppPolicy(appId); final boolean uidForeground = isUidForeground(uid); diff --git a/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java index 9ddf011..60666b4 100644 --- a/services/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/java/com/android/server/net/NetworkStatsCollection.java @@ -29,8 +29,8 @@ import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; import android.text.format.DateUtils; +import android.util.AtomicFile; -import com.android.internal.os.AtomicFile; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Objects; diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index ba122ec..ffe2a3d 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -763,7 +763,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** * Observer that watches for {@link INetworkManagementService} alerts. */ - private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() { + private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() { @Override public void limitReached(String limitName, String iface) { // only someone like NMS should be calling us diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index 48004bb..d4fe3fb 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -338,12 +338,14 @@ class Installer { return execute(builder.toString()); } - public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, + public int getSizeInfo(String pkgName, int persona, String apkPath, String fwdLockApkPath, String asecPath, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); builder.append(' '); + builder.append(persona); + builder.append(' '); builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index f914271..b84e25a 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -64,10 +64,13 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageUserState; +import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageStats; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; @@ -76,8 +79,8 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; -import android.content.pm.UserInfo; import android.content.pm.ManifestDigest; +import android.content.pm.VerificationParams; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; import android.net.Uri; @@ -100,7 +103,7 @@ import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UserId; +import android.os.UserHandle; import android.provider.Settings.Secure; import android.security.SystemKeyStore; import android.util.DisplayMetrics; @@ -165,6 +168,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final boolean DEBUG_UPGRADE = false; private static final boolean DEBUG_INSTALL = false; private static final boolean DEBUG_REMOVE = false; + private static final boolean DEBUG_BROADCASTS = false; private static final boolean DEBUG_SHOW_INFO = false; private static final boolean DEBUG_PACKAGE_INFO = false; private static final boolean DEBUG_INTENT_MATCHING = false; @@ -175,6 +179,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; + private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID; private static final boolean GET_CERTIFICATES = true; @@ -210,7 +215,15 @@ public class PackageManagerService extends IPackageManager.Stub { * The default maximum time to wait for the verification agent to return in * milliseconds. */ - private static final long DEFAULT_VERIFICATION_TIMEOUT = 60 * 1000; + private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000; + + /** + * The default response for package verification timeout. + * + * This can be either PackageManager.VERIFICATION_ALLOW or + * PackageManager.VERIFICATION_REJECT. + */ + private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW; static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; @@ -388,6 +401,7 @@ public class PackageManagerService extends IPackageManager.Stub { // package uri's from external media onto secure containers // or internal storage. private IMediaContainerService mContainerService = null; + private int mContainerServiceUserId; static final int SEND_PENDING_BROADCAST = 1; static final int MCS_BOUND = 3; @@ -410,7 +424,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Delay time in millisecs static final int BROADCAST_DELAY = 10 * 1000; - static UserManager sUserManager; + static UserManagerService sUserManager; // Stores a list of users whose package restrictions file needs to be updated private HashSet<Integer> mDirtyUsers = new HashSet<Integer>(); @@ -456,8 +470,15 @@ public class PackageManagerService extends IPackageManager.Stub { " DefaultContainerService"); Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + mContainerServiceUserId = 0; + if (mPendingInstalls.size() > 0) { + mContainerServiceUserId = mPendingInstalls.get(0).getUser().getIdentifier(); + if (mContainerServiceUserId == UserHandle.USER_ALL) { + mContainerServiceUserId = 0; + } + } if (mContext.bindService(service, mDefContainerConn, - Context.BIND_AUTO_CREATE)) { + Context.BIND_AUTO_CREATE, mContainerServiceUserId)) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mBound = true; return true; @@ -534,6 +555,15 @@ public class PackageManagerService extends IPackageManager.Stub { } else if (mPendingInstalls.size() > 0) { HandlerParams params = mPendingInstalls.get(0); if (params != null) { + // Check if we're connected to the correct service, if it's an install + // request. + final int installFor = params.getUser().getIdentifier(); + if (installFor != mContainerServiceUserId + && (installFor == UserHandle.USER_ALL + && mContainerServiceUserId != 0)) { + mHandler.sendEmptyMessage(MCS_RECONNECT); + return; + } if (params.startCopy()) { // We are done... look for more work or to // go idle. @@ -650,15 +680,21 @@ public class PackageManagerService extends IPackageManager.Stub { break; } case START_CLEANING_PACKAGE: { - String packageName = (String)msg.obj; Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + PackageCleanItem item = new PackageCleanItem((String)msg.obj, + msg.arg2 != 0); synchronized (mPackages) { - if (!mSettings.mPackagesToBeCleaned.contains(packageName)) { - mSettings.mPackagesToBeCleaned.add(packageName); + if (msg.arg1 == UserHandle.USER_ALL) { + int[] users = sUserManager.getUserIds(); + for (int user : users) { + mSettings.addPackageToCleanLPw(user, item); + } + } else { + mSettings.addPackageToCleanLPw(msg.arg1, item); } } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - startCleaningPackages(); + startCleaningPackages(-1); } break; case POST_INSTALL: { if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1); @@ -680,20 +716,23 @@ public class PackageManagerService extends IPackageManager.Stub { } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, res.users); if (update) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, res.pkg.applicationInfo.packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, res.users); sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, - res.pkg.applicationInfo.packageName, null, - UserId.USER_ALL); + res.pkg.applicationInfo.packageName, null, res.users); } if (res.removedInfo.args != null) { // Remove the replaced package's older resources safely now deleteOld = true; } + + // Log current value of "unknown sources" setting + EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, + getUnknownSourcesSettings()); } // Force a gc to clear up things Runtime.getRuntime().gc(); @@ -764,17 +803,28 @@ public class PackageManagerService extends IPackageManager.Stub { final int verificationId = msg.arg1; final PackageVerificationState state = mPendingVerification.get(verificationId); - if (state != null) { + if ((state != null) && !state.timeoutExtended()) { final InstallArgs args = state.getInstallArgs(); Slog.i(TAG, "Verification timed out for " + args.packageURI.toString()); mPendingVerification.remove(verificationId); - int ret = PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT; - processPendingInstall(args, ret); + int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; + + if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) { + Slog.i(TAG, "Continuing with installation of " + + args.packageURI.toString()); + state.setVerifierResponse(Binder.getCallingUid(), + PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); + try { + ret = args.copyApk(mContainerService, true); + } catch (RemoteException e) { + Slog.e(TAG, "Could not contact the ContainerService"); + } + } + processPendingInstall(args, ret); mHandler.sendEmptyMessage(MCS_UNBIND); } - break; } case PACKAGE_VERIFIED: { @@ -874,12 +924,13 @@ public class PackageManagerService extends IPackageManager.Stub { mOnlyCore = onlyCore; mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); - mSettings = new Settings(); + mSettings = new Settings(context); mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM); + mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM); String separateProcesses = SystemProperties.get("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { @@ -916,11 +967,12 @@ public class PackageManagerService extends IPackageManager.Stub { mUserAppDataDir = new File(dataDir, "user"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); - sUserManager = new UserManager(mInstaller, mUserAppDataDir); + sUserManager = new UserManagerService(context, this, + mInstallLock, mPackages); readPermissions(); - mRestoredSettings = mSettings.readLPw(getUsers()); + mRestoredSettings = mSettings.readLPw(sUserManager.getUsers()); long startTime = SystemClock.uptimeMillis(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, @@ -1110,8 +1162,7 @@ public class PackageManagerService extends IPackageManager.Stub { String msg = "System package " + ps.name + " no longer exists; wiping its data"; reportSettingsProblem(Log.WARN, msg); - mInstaller.remove(ps.name, 0); - sUserManager.removePackageForAllUsers(ps.name); + removeDataDirsLI(ps.name); } else { final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); if (disabledPs.codePath == null || !disabledPs.codePath.exists()) { @@ -1160,9 +1211,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (deletedPkg == null) { msg = "Updated system package " + deletedAppName + " no longer exists; wiping its data"; - - mInstaller.remove(deletedAppName, 0); - sUserManager.removePackageForAllUsers(deletedAppName); + removeDataDirsLI(deletedAppName); } else { msg = "Updated system app + " + deletedAppName + " no longer present; removing system privileges for " @@ -1297,13 +1346,7 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); - int retCode = mInstaller.remove(ps.name, 0); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove app data directory for package: " - + ps.name + ", retcode=" + retCode); - } else { - sUserManager.removePackageForAllUsers(ps.name); - } + removeDataDirsLI(ps.name); if (ps.codePath != null) { if (!ps.codePath.delete()) { Slog.w(TAG, "Unable to remove old code file: " + ps.codePath); @@ -1533,19 +1576,17 @@ public class PackageManagerService extends IPackageManager.Stub { PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) { if (!sUserManager.exists(userId)) return null; PackageInfo pi; - if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { - // The package has been uninstalled but has retained data and resources. - pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId); - } else { - final PackageSetting ps = (PackageSetting) p.mExtras; - if (ps == null) { - return null; - } - final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; - pi = PackageParser.generatePackageInfo(p, gp.gids, flags, - ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions, - ps.getStopped(userId), ps.getEnabled(userId), userId); - pi.applicationInfo.enabledSetting = ps.getEnabled(userId); + final PackageSetting ps = (PackageSetting) p.mExtras; + if (ps == null) { + return null; + } + final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; + final PackageUserState state = ps.readUserState(userId); + pi = PackageParser.generatePackageInfo(p, gp.gids, flags, + ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions, + state, userId); + if (pi != null) { + pi.applicationInfo.enabledSetting = state.enabled; pi.applicationInfo.enabled = pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT || pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED; @@ -1602,14 +1643,14 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); if(p != null) { - return UserId.getUid(userId, p.applicationInfo.uid); + return UserHandle.getUid(userId, p.applicationInfo.uid); } PackageSetting ps = mSettings.mPackages.get(packageName); if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) { return -1; } p = ps.pkg; - return p != null ? UserId.getUid(userId, p.applicationInfo.uid) : -1; + return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1; } } @@ -1712,14 +1753,15 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { if (ps.pkg == null) { - PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags, userId); + PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, + flags, userId); if (pInfo != null) { return pInfo.applicationInfo; } return null; } - return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateApplicationInfo(ps.pkg, flags, + ps.readUserState(userId), userId); } return null; } @@ -1729,20 +1771,23 @@ public class PackageManagerService extends IPackageManager.Stub { if (!sUserManager.exists(userId)) return null; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { - PackageParser.Package pkg = new PackageParser.Package(packageName); - if (ps.pkg == null) { - ps.pkg = new PackageParser.Package(packageName); - ps.pkg.applicationInfo.packageName = packageName; - ps.pkg.applicationInfo.flags = ps.pkgFlags; - ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString; - ps.pkg.applicationInfo.sourceDir = ps.codePathString; - ps.pkg.applicationInfo.dataDir = + PackageParser.Package pkg = ps.pkg; + if (pkg == null) { + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) { + return null; + } + pkg = new PackageParser.Package(packageName); + pkg.applicationInfo.packageName = packageName; + pkg.applicationInfo.flags = ps.pkgFlags; + pkg.applicationInfo.publicSourceDir = ps.resourcePathString; + pkg.applicationInfo.sourceDir = ps.codePathString; + pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg.packageName, 0).getPath(); - ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; + pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; } - // ps.pkg.mSetEnabled = ps.getEnabled(userId); - // ps.pkg.mSetStopped = ps.getStopped(userId); - return generatePackageInfo(ps.pkg, flags, userId); + // pkg.mSetEnabled = ps.getEnabled(userId); + // pkg.mSetStopped = ps.getStopped(userId); + return generatePackageInfo(pkg, flags, userId); } return null; } @@ -1760,13 +1805,12 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; // Note: isEnabledLP() does not apply here - always return info - return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId), - ps.getEnabled(userId)); + return PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId)); } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; } - if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { return generateApplicationInfoFromSettingsLPw(packageName, flags, userId); } } @@ -1782,9 +1826,11 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); + synchronized (mInstallLock) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); + } } if (observer != null) { try { @@ -1805,9 +1851,11 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); + synchronized (mInstallLock) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); + } } if(pi != null) { try { @@ -1833,8 +1881,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId), + userId); } if (mResolveComponentName.equals(component)) { return mResolveActivity; @@ -1853,8 +1901,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId), + userId); } } return null; @@ -1870,8 +1918,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId), + userId); } } return null; @@ -1887,8 +1935,8 @@ public class PackageManagerService extends IPackageManager.Stub { if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId), + userId); } } return null; @@ -1933,7 +1981,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private void checkValidCaller(int uid, int userId) { - if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) + if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return; throw new SecurityException("Caller uid=" + uid @@ -1962,7 +2010,7 @@ public class PackageManagerService extends IPackageManager.Stub { public int checkUidPermission(String permName, int uid) { synchronized (mPackages) { - Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid)); + Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj != null) { GrantedPermissions gp = (GrantedPermissions)obj; if (gp.grantedPermissions.contains(permName)) { @@ -1996,7 +2044,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (permName != null) { BasePermission bp = findPermissionTreeLP(permName); if (bp != null) { - if (bp.uid == UserId.getAppId(Binder.getCallingUid())) { + if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) { return bp; } throw new SecurityException("Calling uid " @@ -2199,8 +2247,8 @@ public class PackageManagerService extends IPackageManager.Stub { public int checkUidSignatures(int uid1, int uid2) { // Map to base uids. - uid1 = UserId.getAppId(uid1); - uid2 = UserId.getAppId(uid2); + uid1 = UserHandle.getAppId(uid1); + uid2 = UserHandle.getAppId(uid2); // reader synchronized (mPackages) { Signature[] s1; @@ -2258,7 +2306,7 @@ public class PackageManagerService extends IPackageManager.Stub { } public String[] getPackagesForUid(int uid) { - uid = UserId.getAppId(uid); + uid = UserHandle.getAppId(uid); // reader synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(uid); @@ -2283,7 +2331,7 @@ public class PackageManagerService extends IPackageManager.Stub { public String getNameForUid(int uid) { // reader synchronized (mPackages) { - Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid)); + Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.name + ":" + sus.userId; @@ -2394,6 +2442,9 @@ 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; } @@ -2763,7 +2814,7 @@ public class PackageManagerService extends IPackageManager.Stub { final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>(); final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; final String[] keys; - int userId = UserId.getCallingUserId(); + int userId = UserHandle.getCallingUserId(); // writer synchronized (mPackages) { @@ -2838,8 +2889,8 @@ public class PackageManagerService extends IPackageManager.Stub { } else { final PackageParser.Package p = mPackages.get(packageName); if (p != null && ps != null) { - ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId), - ps.getEnabled(userId), userId); + ai = PackageParser.generateApplicationInfo(p, flags, + ps.readUserState(userId), userId); } } @@ -2862,17 +2913,20 @@ public class PackageManagerService extends IPackageManager.Stub { // reader synchronized (mPackages) { final Iterator<PackageParser.Package> i = mPackages.values().iterator(); - final int userId = UserId.getCallingUserId(); + final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo != null && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p))) { PackageSetting ps = mSettings.mPackages.get(p.packageName); - finalList.add(PackageParser.generateApplicationInfo(p, flags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId)); + if (ps != null) { + ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, + ps.readUserState(userId), userId); + if (ai != null) { + finalList.add(ai); + } + } } } } @@ -2889,14 +2943,12 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = provider != null ? mSettings.mPackages.get(provider.owner.packageName) : null; - return provider != null + return ps != null && mSettings.isEnabledLPr(provider.info, flags, userId) && (!mSafeMode || (provider.info.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) != 0) ? PackageParser.generateProviderInfo(provider, flags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId) + ps.readUserState(userId), userId) : null; } } @@ -2910,20 +2962,21 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet() .iterator(); - final int userId = UserId.getCallingUserId(); + final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { Map.Entry<String, PackageParser.Provider> entry = i.next(); PackageParser.Provider p = entry.getValue(); PackageSetting ps = mSettings.mPackages.get(p.owner.packageName); - if (p.syncable + if (ps != null && p.syncable && (!mSafeMode || (p.info.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) != 0)) { - outNames.add(entry.getKey()); - outInfo.add(PackageParser.generateProviderInfo(p, 0, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId)); + ProviderInfo info = PackageParser.generateProviderInfo(p, 0, + ps.readUserState(userId), userId); + if (info != null) { + outNames.add(entry.getKey()); + outInfo.add(info); + } } } } @@ -2937,24 +2990,25 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator(); final int userId = processName != null ? - UserId.getUserId(uid) : UserId.getCallingUserId(); + UserHandle.getUserId(uid) : UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Provider p = i.next(); PackageSetting ps = mSettings.mPackages.get(p.owner.packageName); - if (p.info.authority != null + if (ps != null && p.info.authority != null && (processName == null || (p.info.processName.equals(processName) - && UserId.isSameApp(p.info.applicationInfo.uid, uid))) + && UserHandle.isSameApp(p.info.applicationInfo.uid, uid))) && mSettings.isEnabledLPr(p.info, flags, userId) && (!mSafeMode || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) { if (finalList == null) { finalList = new ArrayList<ProviderInfo>(3); } - finalList.add(PackageParser.generateProviderInfo(p, flags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId)); + ProviderInfo info = PackageParser.generateProviderInfo(p, flags, + ps.readUserState(userId), userId); + if (info != null) { + finalList.add(info); + } } } } @@ -2987,8 +3041,11 @@ public class PackageManagerService extends IPackageManager.Stub { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { - finalList.add(PackageParser.generateInstrumentationInfo(p, - flags)); + InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, + flags); + if (ii != null) { + finalList.add(ii); + } } } } @@ -3015,7 +3072,7 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } PackageParser.Package pkg = scanPackageLI(file, - flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime); + flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null); // Don't mess around with apps in system partition. if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { @@ -3083,7 +3140,7 @@ public class PackageManagerService extends IPackageManager.Stub { * Returns null in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, - int parseFlags, int scanMode, long currentTime) { + int parseFlags, int scanMode, long currentTime, UserHandle user) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; String scanPath = scanFile.getPath(); parseFlags |= mDefParseFlags; @@ -3149,7 +3206,7 @@ public class PackageManagerService extends IPackageManager.Stub { InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); - synchronized (mInstaller) { + synchronized (mInstallLock) { args.cleanUpResourcesLI(); } synchronized (mPackages) { @@ -3183,7 +3240,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { - deletePackageLI(pkg.packageName, true, 0, null, false); + deletePackageLI(pkg.packageName, null, true, 0, null, false); ps = null; } else { /* @@ -3205,7 +3262,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); - synchronized (mInstaller) { + synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } @@ -3236,7 +3293,7 @@ public class PackageManagerService extends IPackageManager.Stub { setApplicationInfoPaths(pkg, codePath, resPath); // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode - | SCAN_UPDATE_SIGNATURE, currentTime); + | SCAN_UPDATE_SIGNATURE, currentTime, user); /* * If the system app should be overridden by a previously installed @@ -3439,8 +3496,38 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private int createDataDirsLI(String packageName, int uid) { + int[] users = sUserManager.getUserIds(); + int res = mInstaller.install(packageName, uid, uid); + if (res < 0) { + return res; + } + for (int user : users) { + if (user != 0) { + res = mInstaller.createUserData(packageName, + UserHandle.getUid(user, uid), user); + if (res < 0) { + return res; + } + } + } + return res; + } + + private int removeDataDirsLI(String packageName) { + int[] users = sUserManager.getUserIds(); + int res = 0; + for (int user : users) { + int resInner = mInstaller.remove(packageName, user); + if (resInner < 0) { + res = resInner; + } + } + return res; + } + private PackageParser.Package scanPackageLI(PackageParser.Package pkg, - int parseFlags, int scanMode, long currentTime) { + int parseFlags, int scanMode, long currentTime, UserHandle user) { File scanFile = new File(pkg.mScanPath); if (scanFile == null || pkg.applicationInfo.sourceDir == null || pkg.applicationInfo.publicSourceDir == null) { @@ -3632,7 +3719,7 @@ public class PackageManagerService extends IPackageManager.Stub { // the PkgSetting exists already and doesn't have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, - pkg.applicationInfo.flags, true, false); + pkg.applicationInfo.flags, user, false); if (pkgSetting == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; @@ -3791,11 +3878,9 @@ public class PackageManagerService extends IPackageManager.Stub { || (scanMode&SCAN_BOOTING) != 0)) { // If this is a system app, we can at least delete its // current data so the application will still work. - int ret = mInstaller.remove(pkgName, 0); + int ret = removeDataDirsLI(pkgName); if (ret >= 0) { // TODO: Kill the processes first - // Remove the data directories for all users - sUserManager.removePackageForAllUsers(pkgName); // Old data gone! String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0 ? "System package " : "Third party package "; @@ -3807,8 +3892,7 @@ public class PackageManagerService extends IPackageManager.Stub { recovered = true; // And now re-install the app. - ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); + ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret == -1) { // Ack should not happen! msg = prefix + pkg.packageName @@ -3817,9 +3901,6 @@ public class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } - // Create data directories for all users - sUserManager.installPackageForAllUsers(pkgName, - pkg.applicationInfo.uid); } if (!recovered) { mHasSystemUidErrors = true; @@ -3857,15 +3938,12 @@ public class PackageManagerService extends IPackageManager.Stub { Log.v(TAG, "Want this data dir: " + dataPath); } //invoke installer to do the actual installation - int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); + int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret < 0) { // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } - // Create data directories for all users - sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); @@ -3992,7 +4070,9 @@ public class PackageManagerService extends IPackageManager.Stub { // Add the new setting to mPackages mPackages.put(pkg.applicationInfo.packageName, pkg); // Make sure we don't accidentally delete its data. - mSettings.mPackagesToBeCleaned.remove(pkgName); + for (int i=0; i<mSettings.mPackagesToBeCleaned.size(); i++) { + mSettings.mPackagesToBeCleaned.valueAt(i).remove(pkgName); + } // Take care of first install / last update times. if (currentTime != 0) { @@ -4716,14 +4796,17 @@ public class PackageManagerService extends IPackageManager.Stub { mFlags = flags; final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; final int N = packageActivities.size(); - ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut = - new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N); + ArrayList<PackageParser.ActivityIntentInfo[]> listCut = + new ArrayList<PackageParser.ActivityIntentInfo[]>(N); ArrayList<PackageParser.ActivityIntentInfo> intentFilters; for (int i = 0; i < N; ++i) { intentFilters = packageActivities.get(i).intents; if (intentFilters != null && intentFilters.size() > 0) { - listCut.add(intentFilters); + PackageParser.ActivityIntentInfo[] array = + new PackageParser.ActivityIntentInfo[intentFilters.size()]; + intentFilters.toArray(array); + listCut.add(array); } } return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); @@ -4791,6 +4874,11 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + protected ActivityIntentInfo[] newArray(int size) { + return new ActivityIntentInfo[size]; + } + + @Override protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) return true; PackageParser.Package p = filter.activity.owner; @@ -4800,7 +4888,8 @@ public class PackageManagerService extends IPackageManager.Stub { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. - return ps.getStopped(userId) && (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0; + return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 + && ps.getStopped(userId); } } return false; @@ -4823,12 +4912,17 @@ public class PackageManagerService extends IPackageManager.Stub { &ApplicationInfo.FLAG_SYSTEM) == 0) { return null; } - final ResolveInfo res = new ResolveInfo(); PackageSetting ps = (PackageSetting) activity.owner.mExtras; - res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId); + if (ps == null) { + return null; + } + ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags, + ps.readUserState(userId), userId); + if (ai == null) { + return null; + } + final ResolveInfo res = new ResolveInfo(); + res.activityInfo = ai; if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = info; } @@ -4904,14 +4998,17 @@ public class PackageManagerService extends IPackageManager.Stub { mFlags = flags; final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; final int N = packageServices.size(); - ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut = - new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N); + ArrayList<PackageParser.ServiceIntentInfo[]> listCut = + new ArrayList<PackageParser.ServiceIntentInfo[]>(N); ArrayList<PackageParser.ServiceIntentInfo> intentFilters; for (int i = 0; i < N; ++i) { intentFilters = packageServices.get(i).intents; if (intentFilters != null && intentFilters.size() > 0) { - listCut.add(intentFilters); + PackageParser.ServiceIntentInfo[] array = + new PackageParser.ServiceIntentInfo[intentFilters.size()]; + intentFilters.toArray(array); + listCut.add(array); } } return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); @@ -4974,6 +5071,11 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + protected PackageParser.ServiceIntentInfo[] newArray(int size) { + return new PackageParser.ServiceIntentInfo[size]; + } + + @Override protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) return true; PackageParser.Package p = filter.service.owner; @@ -4983,8 +5085,8 @@ public class PackageManagerService extends IPackageManager.Stub { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. - return ps.getStopped(userId) - && (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0; + return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0 + && ps.getStopped(userId); } } return false; @@ -5008,12 +5110,17 @@ public class PackageManagerService extends IPackageManager.Stub { &ApplicationInfo.FLAG_SYSTEM) == 0) { return null; } - final ResolveInfo res = new ResolveInfo(); PackageSetting ps = (PackageSetting) service.owner.mExtras; - res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags, - ps != null ? ps.getStopped(userId) : false, - ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT, - userId); + if (ps == null) { + return null; + } + ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags, + ps.readUserState(userId), userId); + if (si == null) { + return null; + } + final ResolveInfo res = new ResolveInfo(); + res.serviceInfo = si; if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = filter; } @@ -5104,13 +5211,14 @@ public class PackageManagerService extends IPackageManager.Stub { }; static final void sendPackageBroadcast(String action, String pkg, - Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) { + Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, + int[] userIds) { IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { - int[] userIds = userId == UserId.USER_ALL - ? sUserManager.getUserIds() - : new int[] {userId}; + if (userIds == null) { + userIds = sUserManager.getUserIds(); + } for (int id : userIds) { final Intent intent = new Intent(action, pkg != null ? Uri.fromParts("package", pkg, null) : null); @@ -5122,11 +5230,18 @@ public class PackageManagerService extends IPackageManager.Stub { } // Modify the UID when posting to other users int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (uid > 0 && id > 0) { - uid = UserId.getUid(id, UserId.getAppId(uid)); + if (uid > 0 && UserHandle.getUserId(uid) != id) { + uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); intent.putExtra(Intent.EXTRA_UID, uid); } intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (DEBUG_BROADCASTS) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "Sending to user " + id + ": " + + intent.toShortString(false, true, false, false) + + " " + intent.getExtras(), here); + } am.broadcastIntent(null, intent, null, finishedReceiver, 0, null, null, null, finishedReceiver != null, false, id); } @@ -5144,8 +5259,9 @@ public class PackageManagerService extends IPackageManager.Stub { return mMediaMounted || Environment.isExternalStorageEmulated(); } - public String nextPackageToClean(String lastPackage) { + public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) { // writer + final int userId = UserHandle.getCallingUserId(); synchronized (mPackages) { if (!isExternalMediaAvailable()) { // If the external storage is no longer mounted at this point, @@ -5153,34 +5269,65 @@ public class PackageManagerService extends IPackageManager.Stub { // packages files and can not delete any more. Bail. return null; } - if (lastPackage != null) { - mSettings.mPackagesToBeCleaned.remove(lastPackage); + ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned.get(userId); + if (pkgs != null) { + if (lastPackage != null) { + pkgs.remove(lastPackage); + } + if (pkgs.size() > 0) { + return pkgs.get(0); + } } - return mSettings.mPackagesToBeCleaned.size() > 0 - ? mSettings.mPackagesToBeCleaned.get(0) : null; } + // Move on to the next user to clean. + long ident = Binder.clearCallingIdentity(); + try { + startCleaningPackages(userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + return null; } - void schedulePackageCleaning(String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName)); + void schedulePackageCleaning(String packageName, int userId, boolean andCode) { + if (false) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId + + " andCode=" + andCode, here); + } + mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, + userId, andCode ? 1 : 0, packageName)); } - void startCleaningPackages() { + void startCleaningPackages(int lastUser) { // reader + int nextUser = -1; synchronized (mPackages) { if (!isExternalMediaAvailable()) { return; } - if (mSettings.mPackagesToBeCleaned.size() <= 0) { + final int N = mSettings.mPackagesToBeCleaned.size(); + if (N <= 0) { return; } + for (int i=0; i<N; i++) { + int user = mSettings.mPackagesToBeCleaned.keyAt(i); + if (user > lastUser) { + nextUser = user; + break; + } + } + if (nextUser < 0) { + nextUser = mSettings.mPackagesToBeCleaned.keyAt(0); + } } Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE); intent.setComponent(DEFAULT_CONTAINER_COMPONENT); IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { - am.startService(null, intent, null); + am.startService(null, intent, null, nextUser); } catch (RemoteException e) { } } @@ -5243,7 +5390,7 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK, SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME, - System.currentTimeMillis()); + System.currentTimeMillis(), null); if (p != null) { /* * TODO this seems dangerous as the package may have @@ -5272,13 +5419,13 @@ public class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, removedUid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, null); } if (addedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, addedUid); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, null); } } @@ -5304,9 +5451,25 @@ public class PackageManagerService extends IPackageManager.Stub { public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); + VerificationParams verificationParams = new VerificationParams(verificationURI, null, null, + manifestDigest); + installPackageWithVerificationAndEncryption(packageURI, observer, flags, + installerPackageName, verificationParams, encryptionParams); + } + + public void installPackageWithVerificationAndEncryption(Uri packageURI, + IPackageInstallObserver observer, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, + null); final int uid = Binder.getCallingUid(); + UserHandle user; + if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) { + user = UserHandle.ALL; + } else { + user = Process.myUserHandle(); + } final int filteredFlags; @@ -5321,10 +5484,51 @@ public class PackageManagerService extends IPackageManager.Stub { final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName, - verificationURI, manifestDigest, encryptionParams); + verificationParams, encryptionParams, user); mHandler.sendMessage(msg); } + /** + * @hide + */ + @Override + public int installExistingPackage(String packageName) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, + null); + PackageSetting pkgSetting; + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + + long callingId = Binder.clearCallingIdentity(); + try { + boolean sendAdded = false; + Bundle extras = new Bundle(1); + + // writer + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null) { + return PackageManager.INSTALL_FAILED_INVALID_URI; + } + if (!pkgSetting.getInstalled(userId)) { + pkgSetting.setInstalled(true, userId); + mSettings.writePackageRestrictionsLPr(userId); + extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId)); + sendAdded = true; + } + } + + if (sendAdded) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + packageName, extras, null, null, new int[] {userId}); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + + return PackageManager.INSTALL_SUCCEEDED; + } + @Override public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED); @@ -5335,6 +5539,32 @@ public class PackageManagerService extends IPackageManager.Stub { mHandler.sendMessage(msg); } + @Override + public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, + long millisecondsToDelay) { + final PackageVerificationState state = mPendingVerification.get(id); + final PackageVerificationResponse response = new PackageVerificationResponse( + verificationCodeAtTimeout, Binder.getCallingUid()); + + if ((millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) + || (millisecondsToDelay < 0)) { + throw new IllegalArgumentException("millisecondsToDelay is out of bounds."); + } + if ((verificationCodeAtTimeout != PackageManager.VERIFICATION_ALLOW) + || (verificationCodeAtTimeout != PackageManager.VERIFICATION_REJECT)) { + throw new IllegalArgumentException("verificationCodeAtTimeout is unknown."); + } + + if ((state != null) && !state.timeoutExtended()) { + state.extendTimeout(); + + final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED); + msg.arg1 = id; + msg.obj = response; + mHandler.sendMessageDelayed(msg, millisecondsToDelay); + } + } + private ComponentName matchComponentForVerifier(String packageName, List<ResolveInfo> receivers) { ActivityInfo targetReceiver = null; @@ -5453,6 +5683,17 @@ public class PackageManagerService extends IPackageManager.Stub { } /** + * Get the default verification agent response code. + * + * @return default verification response code + */ + private int getDefaultVerificationResponse() { + return android.provider.Settings.Secure.getInt(mContext.getContentResolver(), + android.provider.Settings.Secure.PACKAGE_VERIFIER_DEFAULT_RESPONSE, + DEFAULT_VERIFICATION_RESPONSE); + } + + /** * Check whether or not package verification has been enabled. * * @return true if verification should be performed @@ -5463,6 +5704,17 @@ public class PackageManagerService extends IPackageManager.Stub { DEFAULT_VERIFY_ENABLE ? 1 : 0) == 1 ? true : false; } + /** + * Get the "allow unknown sources" setting. + * + * @return the current "allow unknown sources" setting + */ + private int getUnknownSourcesSettings() { + return android.provider.Settings.Secure.getInt(mContext.getContentResolver(), + android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, + -1); + } + public void setInstallerPackageName(String targetPackage, String installerPackageName) { final int uid = Binder.getCallingUid(); // writer @@ -5615,6 +5867,17 @@ public class PackageManagerService extends IPackageManager.Stub { */ private int mRetries = 0; + /** User handle for the user requesting the information or installation. */ + private final UserHandle mUser; + + HandlerParams(UserHandle user) { + mUser = user; + } + + UserHandle getUser() { + return mUser; + } + final boolean startCopy() { boolean res; try { @@ -5656,6 +5919,7 @@ public class PackageManagerService extends IPackageManager.Stub { private final IPackageStatsObserver mObserver; public MeasureParams(PackageStats stats, IPackageStatsObserver observer) { + super(new UserHandle(stats.userHandle)); mObserver = observer; mStats = stats; } @@ -5663,7 +5927,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override void handleStartCopy() throws RemoteException { synchronized (mInstallLock) { - mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats); + mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats); } final boolean mounted; @@ -5729,8 +5993,7 @@ public class PackageManagerService extends IPackageManager.Stub { private final Uri mPackageURI; final String installerPackageName; - final Uri verificationURI; - final ManifestDigest manifestDigest; + final VerificationParams verificationParams; private InstallArgs mArgs; private int mRet; private File mTempPackage; @@ -5738,17 +6001,24 @@ public class PackageManagerService extends IPackageManager.Stub { InstallParams(Uri packageURI, IPackageInstallObserver observer, int flags, - String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest, - ContainerEncryptionParams encryptionParams) { + String installerPackageName, VerificationParams verificationParams, + ContainerEncryptionParams encryptionParams, UserHandle user) { + super(user); this.mPackageURI = packageURI; this.flags = flags; this.observer = observer; this.installerPackageName = installerPackageName; - this.verificationURI = verificationURI; - this.manifestDigest = manifestDigest; + this.verificationParams = verificationParams; this.encryptionParams = encryptionParams; } + public ManifestDigest getManifestDigest() { + if (verificationParams == null) { + return null; + } + return verificationParams.getManifestDigest(); + } + private int installLocationPolicy(PackageInfoLite pkgLite, int flags) { String packageName = pkgLite.packageName; int installLocation = pkgLite.installLocation; @@ -5758,6 +6028,16 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Package pkg = mPackages.get(packageName); if (pkg != null) { if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + // Check for downgrading. + if ((flags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) { + if (pkgLite.versionCode < pkg.mVersionCode) { + Slog.w(TAG, "Can't install update of " + packageName + + " update version " + pkgLite.versionCode + + " is older than installed version " + + pkg.mVersionCode); + return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE; + } + } // Check for updated system application. if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (onSd) { @@ -5884,6 +6164,8 @@ public class PackageManagerService extends IPackageManager.Stub { ret = PackageManager.INSTALL_FAILED_INVALID_URI; } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; + } else if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { + ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; } else { // Override with defaults if needed. loc = installLocationPolicy(pkgLite, flags); @@ -5919,8 +6201,9 @@ public class PackageManagerService extends IPackageManager.Stub { verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - final List<ResolveInfo> receivers = queryIntentReceivers(verification, null, - PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */); + final List<ResolveInfo> receivers = queryIntentReceivers(verification, + PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS, + 0 /* TODO: Which userId? */); if (DEBUG_VERIFY) { Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent " @@ -5937,9 +6220,19 @@ public class PackageManagerService extends IPackageManager.Stub { verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, flags); - if (verificationURI != null) { - verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI, - verificationURI); + if (verificationParams != null) { + if (verificationParams.getVerificationURI() != null) { + verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI, + verificationParams.getVerificationURI()); + } + if (verificationParams.getOriginatingURI() != null) { + verification.putExtra(Intent.EXTRA_ORIGINATING_URI, + verificationParams.getOriginatingURI()); + } + if (verificationParams.getReferrer() != null) { + verification.putExtra(Intent.EXTRA_REFERRER, + verificationParams.getReferrer()); + } } final PackageVerificationState verificationState = new PackageVerificationState( @@ -6064,7 +6357,8 @@ public class PackageManagerService extends IPackageManager.Stub { int mRet; MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags, - String packageName, String dataDir, int uid) { + String packageName, String dataDir, int uid, UserHandle user) { + super(user); this.srcArgs = srcArgs; this.observer = observer; this.flags = flags; @@ -6217,14 +6511,17 @@ public class PackageManagerService extends IPackageManager.Stub { final Uri packageURI; final String installerPackageName; final ManifestDigest manifestDigest; + final UserHandle user; InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags, - String installerPackageName, ManifestDigest manifestDigest) { + String installerPackageName, ManifestDigest manifestDigest, + UserHandle user) { this.packageURI = packageURI; this.flags = flags; this.observer = observer; this.installerPackageName = installerPackageName; this.manifestDigest = manifestDigest; + this.user = user; } abstract void createCopyFile(); @@ -6275,11 +6572,12 @@ public class PackageManagerService extends IPackageManager.Stub { FileInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.flags, - params.installerPackageName, params.manifestDigest); + params.installerPackageName, params.getManifestDigest(), + params.getUser()); } FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) { - super(null, null, 0, null, null); + super(null, null, 0, null, null, null); File codeFile = new File(fullCodePath); installDir = codeFile.getParentFile(); codeFileName = fullCodePath; @@ -6288,7 +6586,7 @@ public class PackageManagerService extends IPackageManager.Stub { } FileInstallArgs(Uri packageURI, String pkgName, String dataDir) { - super(packageURI, null, 0, null, null); + super(packageURI, null, 0, null, null, null); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); @@ -6562,13 +6860,15 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.flags, - params.installerPackageName, params.manifestDigest); + params.installerPackageName, params.getManifestDigest(), + params.getUser()); } AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, boolean isExternal, boolean isForwardLocked) { super(null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) - | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null); + | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), + null, null, null); // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); @@ -6579,14 +6879,16 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(String cid, boolean isForwardLocked) { super(null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0) - | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null); + | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), + null, null, null); this.cid = cid; setCachePath(PackageHelper.getSdDir(cid)); } AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) { super(packageURI, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) - | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null); + | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), + null, null, null); this.cid = cid; } @@ -6910,6 +7212,7 @@ public class PackageManagerService extends IPackageManager.Stub { class PackageInstalledInfo { String name; int uid; + int[] users; PackageParser.Package pkg; int returnCode; PackageRemovedInfo removedInfo; @@ -6919,14 +7222,12 @@ public class PackageManagerService extends IPackageManager.Stub { * Install a non-existing package. */ private void installNewPackageLI(PackageParser.Package pkg, - int parseFlags, - int scanMode, + int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists(); - res.name = pkgName; synchronized(mPackages) { if (mSettings.mRenamedPackages.containsKey(pkgName)) { // A package with the same name is already installed, though @@ -6949,7 +7250,7 @@ public class PackageManagerService extends IPackageManager.Stub { } mLastScanError = PackageManager.INSTALL_SUCCEEDED; PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, - System.currentTimeMillis()); + System.currentTimeMillis(), user); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -6966,17 +7267,15 @@ public class PackageManagerService extends IPackageManager.Stub { // delete the package data and cache directories that it created in // scanPackageLocked, unless those directories existed before we even tried to // install. - deletePackageLI( - pkgName, false, - dataDirExists ? PackageManager.DONT_DELETE_DATA : 0, + deletePackageLI(pkgName, UserHandle.ALL, false, + dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0, res.removedInfo, true); } } } private void replacePackageLI(PackageParser.Package pkg, - int parseFlags, - int scanMode, + int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package oldPackage; @@ -6993,15 +7292,16 @@ public class PackageManagerService extends IPackageManager.Stub { } boolean sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { - replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res); + replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, + user, installerPackageName, res); } else { - replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res); + replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, + user, installerPackageName, res); } } private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, - PackageParser.Package pkg, - int parseFlags, int scanMode, + PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package newPackage = null; String pkgName = deletedPackage.packageName; @@ -7016,7 +7316,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // First delete the existing package while retaining the data directory - if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA, + if (!deletePackageLI(pkgName, null, true, PackageManager.DELETE_KEEP_DATA, res.removedInfo, true)) { // If the existing package wasn't successfully deleted res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; @@ -7025,7 +7325,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully deleted the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME, - System.currentTimeMillis()); + System.currentTimeMillis(), user); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -7046,8 +7346,8 @@ public class PackageManagerService extends IPackageManager.Stub { // install. if(updatedSettings) { deletePackageLI( - pkgName, true, - PackageManager.DONT_DELETE_DATA, + pkgName, null, true, + PackageManager.DELETE_KEEP_DATA, res.removedInfo, true); } // Since we failed to install the new package we need to restore the old @@ -7062,7 +7362,7 @@ public class PackageManagerService extends IPackageManager.Stub { int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode, - origUpdateTime) == null) { + origUpdateTime, user) == null) { Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade"); return; } @@ -7080,8 +7380,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private void replaceSystemPackageLI(PackageParser.Package deletedPackage, - PackageParser.Package pkg, - int parseFlags, int scanMode, + PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { PackageParser.Package newPackage = null; boolean updatedSettings = false; @@ -7130,7 +7429,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully disabled the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0); + newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -7153,7 +7452,7 @@ public class PackageManagerService extends IPackageManager.Stub { removePackageLI(newPackage, true); } // Add back the old system package - scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0); + scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user); // Restore the old system information in Settings synchronized(mPackages) { if (updatedSettings) { @@ -7212,6 +7511,10 @@ public class PackageManagerService extends IPackageManager.Stub { UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0)); res.name = pkgName; + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + res.users = ps.getInstalledUsers(sUserManager.getUserIds()); + } res.uid = newPackage.applicationInfo.uid; res.pkg = newPackage; mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE); @@ -7327,12 +7630,18 @@ public class PackageManagerService extends IPackageManager.Stub { setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath()); pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); if (replace) { - replacePackageLI(pkg, parseFlags, scanMode, + replacePackageLI(pkg, parseFlags, scanMode, args.user, installerPackageName, res); } else { - installNewPackageLI(pkg, parseFlags, scanMode, + installNewPackageLI(pkg, parseFlags, scanMode, args.user, installerPackageName,res); } + synchronized (mPackages) { + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + res.users = ps.getInstalledUsers(sUserManager.getUserIds()); + } + } } private static boolean isForwardLocked(PackageParser.Package pkg) { @@ -7423,10 +7732,11 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_PACKAGES, null); // Queue up an async operation since the package deletion may take a little while. + final int uid = Binder.getCallingUid(); mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - final int returnCode = deletePackageX(packageName, true, true, flags); + final int returnCode = deletePackageX(packageName, uid, flags); if (observer != null) { try { observer.packageDeleted(packageName, returnCode); @@ -7452,8 +7762,7 @@ public class PackageManagerService extends IPackageManager.Stub { * persisting settings for later use * sending a broadcast if necessary */ - private int deletePackageX(String packageName, boolean sendBroadCast, - boolean deleteCodeAndResources, int flags) { + private int deletePackageX(String packageName, int uid, int flags) { final PackageRemovedInfo info = new PackageRemovedInfo(); final boolean res; @@ -7468,27 +7777,30 @@ public class PackageManagerService extends IPackageManager.Stub { } synchronized (mInstallLock) { - res = deletePackageLI(packageName, deleteCodeAndResources, - flags | REMOVE_CHATTY, info, true); + res = deletePackageLI(packageName, + (flags & PackageManager.DELETE_ALL_USERS) != 0 + ? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)), + true, flags | REMOVE_CHATTY, info, true); } - if (res && sendBroadCast) { + if (res) { boolean systemUpdate = info.isRemovedPackageSystemUpdate; - info.sendBroadcast(deleteCodeAndResources, systemUpdate); + info.sendBroadcast(true, systemUpdate); // If the removed package was a system update, the old system packaged // was re-enabled; we need to broadcast this information if (systemUpdate) { Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid); + extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0 + ? info.removedAppId : info.uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, null); sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, - extras, null, null, UserId.USER_ALL); + extras, null, null, null); sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, - null, packageName, null, UserId.USER_ALL); + null, packageName, null, null); } } // Force a gc here. @@ -7497,7 +7809,7 @@ public class PackageManagerService extends IPackageManager.Stub { // other processes clean up before deleting resources. if (info.args != null) { synchronized (mInstallLock) { - info.args.doPostDeleteLI(deleteCodeAndResources); + info.args.doPostDeleteLI(true); } } @@ -7507,29 +7819,30 @@ public class PackageManagerService extends IPackageManager.Stub { static class PackageRemovedInfo { String removedPackage; int uid = -1; - int removedUid = -1; + int removedAppId = -1; + int[] removedUsers = null; boolean isRemovedPackageSystemUpdate = false; // Clean up resources deleted packages. InstallArgs args = null; void sendBroadcast(boolean fullRemove, boolean replacing) { Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid); + extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove); if (replacing) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } if (removedPackage != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, removedUsers); if (fullRemove && !replacing) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, - extras, null, null, UserId.USER_ALL); + extras, null, null, removedUsers); } } - if (removedUid >= 0) { + if (removedAppId >= 0) { sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null, - UserId.getUserId(removedUid)); + removedUsers); } } } @@ -7543,34 +7856,28 @@ public class PackageManagerService extends IPackageManager.Stub { private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo, int flags, boolean writeSettings) { String packageName = p.packageName; - if (outInfo != null) { - outInfo.removedPackage = packageName; - } removePackageLI(p, (flags&REMOVE_CHATTY) != 0); // Retrieve object to delete permissions for shared user later on final PackageSetting deletedPs; // reader synchronized (mPackages) { deletedPs = mSettings.mPackages.get(packageName); - } - if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { - int retCode = mInstaller.remove(packageName, 0); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove app data or cache directory for package: " - + packageName + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion - } else { - // TODO: Kill the processes first - sUserManager.removePackageForAllUsers(packageName); + if (outInfo != null) { + outInfo.removedPackage = packageName; + outInfo.removedUsers = deletedPs != null + ? deletedPs.getInstalledUsers(sUserManager.getUserIds()) : null; } - schedulePackageCleaning(packageName); + } + if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { + removeDataDirsLI(packageName); + schedulePackageCleaning(packageName, UserHandle.USER_ALL, true); } // writer synchronized (mPackages) { if (deletedPs != null) { - if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { + if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { if (outInfo != null) { - outInfo.removedUid = mSettings.removePackageLPw(packageName); + outInfo.removedAppId = mSettings.removePackageLPw(packageName); } if (deletedPs != null) { updatePermissionsLPw(deletedPs.name, null, 0); @@ -7579,7 +7886,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids); } } - clearPackagePreferredActivitiesLPw(deletedPs.name); + clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL); } } // can downgrade to reader @@ -7619,10 +7926,10 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.isRemovedPackageSystemUpdate = true; if (ps.versionCode < p.mVersionCode) { // Delete data for downgrades - flags &= ~PackageManager.DONT_DELETE_DATA; + flags &= ~PackageManager.DELETE_KEEP_DATA; } else { // Preserve data by setting flag - flags |= PackageManager.DONT_DELETE_DATA; + flags |= PackageManager.DELETE_KEEP_DATA; } boolean ret = deleteInstalledPackageLI(p, true, flags, outInfo, writeSettings); @@ -7639,7 +7946,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Install the system package PackageParser.Package newPkg = scanPackageLI(ps.codePath, PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM, - SCAN_MONITOR | SCAN_NO_PATHS, 0); + SCAN_MONITOR | SCAN_NO_PATHS, 0, null); if (newPkg == null) { Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError); @@ -7686,7 +7993,7 @@ public class PackageManagerService extends IPackageManager.Stub { /* * This method handles package deletion in general */ - private boolean deletePackageLI(String packageName, + private boolean deletePackageLI(String packageName, UserHandle user, boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo, boolean writeSettings) { if (packageName == null) { @@ -7695,6 +8002,8 @@ public class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package p; boolean dataOnly = false; + int removeUser = -1; + int appId = -1; synchronized (mPackages) { p = mPackages.get(packageName); if (p == null) { @@ -7702,15 +8011,53 @@ public class PackageManagerService extends IPackageManager.Stub { dataOnly = true; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); return false; } p = ps.pkg; } + if (p == null) { + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); + return false; + } + final PackageSetting ps = (PackageSetting)p.mExtras; + if (!isSystemApp(p) && ps != null && user != null + && user.getIdentifier() != UserHandle.USER_ALL) { + // The caller is asking that the package only be deleted for a single + // user. To do this, we just mark its uninstalled state and delete + // its data. + ps.setUserState(user.getIdentifier(), + COMPONENT_ENABLED_STATE_DEFAULT, + false, //installed + true, //stopped + true, //notLaunched + null, null); + if (ps.isAnyInstalled(sUserManager.getUserIds())) { + // Other user still have this package installed, so all + // we need to do is clear this user's data and save that + // it is uninstalled. + removeUser = user.getIdentifier(); + appId = ps.appId; + mSettings.writePackageRestrictionsLPr(removeUser); + } else { + // We need to set it back to 'installed' so the uninstall + // broadcasts will be sent correctly. + ps.setInstalled(true, user.getIdentifier()); + } + } } - if (p == null) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); - return false; + + if (removeUser >= 0) { + // From above, we determined that we are deleting this only + // for a single user. Continue the work here. + if (outInfo != null) { + outInfo.removedPackage = packageName; + outInfo.removedAppId = appId; + outInfo.removedUsers = new int[] {removeUser}; + } + mInstaller.clearUserData(packageName, removeUser); + schedulePackageCleaning(packageName, removeUser, false); + return true; } if (dataOnly) { @@ -7755,7 +8102,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private void clearExternalStorageDataSync(String packageName, boolean allData) { + private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) { final boolean mounted; if (Environment.isExternalStorageEmulated()) { mounted = true; @@ -7771,44 +8118,52 @@ public class PackageManagerService extends IPackageManager.Stub { } final Intent containerIntent = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); - ClearStorageConnection conn = new ClearStorageConnection(); - if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE)) { - try { - long timeout = SystemClock.uptimeMillis() + 5000; - synchronized (conn) { - long now = SystemClock.uptimeMillis(); - while (conn.mContainerService == null && now < timeout) { - try { - conn.wait(timeout - now); - } catch (InterruptedException e) { + int[] users; + if (userId == UserHandle.USER_ALL) { + users = sUserManager.getUserIds(); + } else { + users = new int[] { userId }; + } + for (int curUser : users) { + ClearStorageConnection conn = new ClearStorageConnection(); + if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE, curUser)) { + try { + long timeout = SystemClock.uptimeMillis() + 5000; + synchronized (conn) { + long now = SystemClock.uptimeMillis(); + while (conn.mContainerService == null && now < timeout) { + try { + conn.wait(timeout - now); + } catch (InterruptedException e) { + } } } - } - if (conn.mContainerService == null) { - return; - } - final File externalCacheDir = Environment - .getExternalStorageAppCacheDirectory(packageName); - try { - conn.mContainerService.clearDirectory(externalCacheDir.toString()); - } catch (RemoteException e) { - } - if (allData) { - final File externalDataDir = Environment - .getExternalStorageAppDataDirectory(packageName); - try { - conn.mContainerService.clearDirectory(externalDataDir.toString()); - } catch (RemoteException e) { + if (conn.mContainerService == null) { + return; } - final File externalMediaDir = Environment - .getExternalStorageAppMediaDirectory(packageName); + final File externalCacheDir = Environment + .getExternalStorageAppCacheDirectory(packageName); try { - conn.mContainerService.clearDirectory(externalMediaDir.toString()); + conn.mContainerService.clearDirectory(externalCacheDir.toString()); } catch (RemoteException e) { } + if (allData) { + final File externalDataDir = Environment + .getExternalStorageAppDataDirectory(packageName); + try { + conn.mContainerService.clearDirectory(externalDataDir.toString()); + } catch (RemoteException e) { + } + final File externalMediaDir = Environment + .getExternalStorageAppMediaDirectory(packageName); + try { + conn.mContainerService.clearDirectory(externalMediaDir.toString()); + } catch (RemoteException e) { + } + } + } finally { + mContext.unbindService(conn); } - } finally { - mContext.unbindService(conn); } } } @@ -7827,7 +8182,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { succeeded = clearApplicationUserDataLI(packageName, userId); } - clearExternalStorageDataSync(packageName, true); + clearExternalStorageDataSync(packageName, userId, true); if (succeeded) { // invoke DeviceStorageMonitor's update method to clear any notifications DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) @@ -7893,7 +8248,7 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_CACHE_FILES, null); // Queue up an async operation since the package deletion may take a little while. - final int userId = UserId.getCallingUserId(); + final int userId = UserHandle.getCallingUserId(); mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); @@ -7901,7 +8256,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { succeded = deleteApplicationCacheFilesLI(packageName, userId); } - clearExternalStorageDataSync(packageName, false); + clearExternalStorageDataSync(packageName, userId, false); if(observer != null) { try { observer.onRemoveCompleted(packageName, succeded); @@ -7941,12 +8296,12 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } - public void getPackageSizeInfo(final String packageName, + public void getPackageSizeInfo(final String packageName, int userHandle, final IPackageStatsObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GET_PACKAGE_SIZE, null); - PackageStats stats = new PackageStats(packageName); + PackageStats stats = new PackageStats(packageName, userHandle); /* * Queue up an async operation since the package measurement may take a @@ -7957,7 +8312,8 @@ public class PackageManagerService extends IPackageManager.Stub { mHandler.sendMessage(msg); } - private boolean getPackageSizeInfoLI(String packageName, PackageStats pStats) { + private boolean getPackageSizeInfoLI(String packageName, int userHandle, + PackageStats pStats) { if (packageName == null) { Slog.w(TAG, "Attempt to get size of null packageName."); return false; @@ -7994,7 +8350,7 @@ public class PackageManagerService extends IPackageManager.Stub { publicSrcDir = applicationInfo.publicSourceDir; } } - int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, + int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, publicSrcDir, asecPath, pStats); if (res < 0) { return false; @@ -8046,26 +8402,28 @@ public class PackageManagerService extends IPackageManager.Stub { } public void addPreferredActivity(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity) { + ComponentName[] set, ComponentName activity, int userId) { // writer + int callingUid = Binder.getCallingUid(); + checkValidCaller(callingUid, userId); synchronized (mPackages) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid()) + if (getUidTargetSdkVersionLockedLPr(callingUid) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring addPreferredActivity() from uid " - + Binder.getCallingUid()); + + callingUid); return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - - Slog.i(TAG, "Adding preferred activity " + activity + ":"); + + 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)); + new PreferredActivity(filter, match, set, activity, userId)); scheduleWriteSettingsLocked(); } } @@ -8101,13 +8459,15 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - + + 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>(); @@ -8123,7 +8483,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.mPreferredActivities.removeFilter(pa); } } - addPreferredActivity(filter, match, set, activity); + addPreferredActivity(filter, match, set, activity, callingUserId); } } @@ -8147,17 +8507,21 @@ public class PackageManagerService extends IPackageManager.Stub { } } - if (clearPackagePreferredActivitiesLPw(packageName)) { + if (clearPackagePreferredActivitiesLPw(packageName, UserHandle.getCallingUserId())) { scheduleWriteSettingsLocked(); } } } - boolean clearPackagePreferredActivitiesLPw(String packageName) { + /** 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) { + continue; + } if (pa.mPref.mComponent.getPackageName().equals(packageName)) { if (removed == null) { removed = new ArrayList<PreferredActivity>(); @@ -8179,11 +8543,15 @@ public class PackageManagerService extends IPackageManager.Stub { List<ComponentName> outActivities, String packageName) { int num = 0; + 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) { @@ -8248,7 +8616,7 @@ public class PackageManagerService extends IPackageManager.Stub { + "/" + className); } // Allow root and verify that userId is not being specified by a different user - if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.appId)) { + if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) { throw new SecurityException( "Permission Denial: attempt to change component state from pid=" + Binder.getCallingPid() @@ -8297,7 +8665,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } mSettings.writePackageRestrictionsLPr(userId); - packageUid = UserId.getUid(userId, pkgSetting.appId); + packageUid = UserHandle.getUid(userId, pkgSetting.appId); components = mPendingBroadcasts.get(packageName); final boolean newPackage = components == null; if (newPackage) { @@ -8346,7 +8714,7 @@ public class PackageManagerService extends IPackageManager.Stub { extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag); extras.putInt(Intent.EXTRA_UID, packageUid); sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null, - UserId.getUserId(packageUid)); + new int[] {UserHandle.getUserId(packageUid)}); } public void setPackageStoppedState(String packageName, boolean stopped, int userId) { @@ -8951,7 +9319,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); loadMediaPackages(processCids, uidArr, removeCids); - startCleaningPackages(); + startCleaningPackages(-1); } else { if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); @@ -8972,7 +9340,7 @@ public class PackageManagerService extends IPackageManager.Stub { } String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; - sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserId.USER_ALL); + sendPackageBroadcast(action, null, extras, null, finishedReceiver, null); } } @@ -9017,7 +9385,7 @@ public class PackageManagerService extends IPackageManager.Stub { doGc = true; synchronized (mInstallLock) { final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags, - 0, 0); + 0, 0, null); // Scan the package if (pkg != null) { /* @@ -9126,8 +9494,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete package internally PackageRemovedInfo outInfo = new PackageRemovedInfo(); synchronized (mInstallLock) { - boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, - outInfo, false); + boolean res = deletePackageLI(pkgName, null, false, + PackageManager.DELETE_KEEP_DATA, outInfo, false); if (res) { pkgList.add(pkgName); } else { @@ -9164,9 +9532,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** Binder call */ + @Override public void movePackage(final String packageName, final IPackageMoveObserver observer, final int flags) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); + UserHandle user = new UserHandle(UserHandle.getCallingUserId()); int returnCode = PackageManager.MOVE_SUCCEEDED; int currFlags = 0; int newFlags = 0; @@ -9217,14 +9588,15 @@ public class PackageManagerService extends IPackageManager.Stub { * anyway. */ if (returnCode != PackageManager.MOVE_SUCCEEDED) { - processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1), + processPendingMove(new MoveParams(null, observer, 0, packageName, + null, -1, user), returnCode); } else { Message msg = mHandler.obtainMessage(INIT_COPY); InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, - pkg.applicationInfo.dataDir, pkg.applicationInfo.uid); + pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user); msg.obj = mp; mHandler.sendMessage(msg); } @@ -9415,48 +9787,37 @@ public class PackageManagerService extends IPackageManager.Stub { PackageHelper.APP_INSTALL_AUTO); } - public UserInfo createUser(String name, int flags) { - // TODO(kroot): Add a real permission for creating users - enforceSystemOrRoot("Only the system can create users"); - - UserInfo userInfo = sUserManager.createUser(name, flags); - if (userInfo != null) { - Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS); - } - return userInfo; - } - - public boolean removeUser(int userId) { - // TODO(kroot): Add a real permission for removing users - enforceSystemOrRoot("Only the system can remove users"); - - if (userId == 0 || !sUserManager.exists(userId)) { - return false; + /** 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); } - - cleanUpUser(userId); - - if (sUserManager.removeUser(userId)) { - // Let other services shutdown any activity - Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); - addedIntent.putExtra(Intent.EXTRA_USERID, userId); - mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS); + if (mDirtyUsers.remove(userHandle)); + mSettings.removeUserLPr(userHandle); + if (mInstaller != null) { + // Technically, we shouldn't be doing this with the package lock + // held. However, this is very rare, and there is already so much + // other disk I/O going on, that we'll let it slide for now. + mInstaller.removeUserDataDirs(userHandle); } - sUserManager.removePackageFolders(userId); - return true; } - private void cleanUpUser(int userId) { - // Disable all the packages for the user first - synchronized (mPackages) { - Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet(); - for (Entry<String, PackageSetting> entry : entries) { - entry.getValue().removeUser(userId); + /** 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); } - if (mDirtyUsers.remove(userId)); - mSettings.removeUserLPr(userId); + mSettings.writePackageRestrictionsLPr(userHandle); } } @@ -9472,24 +9833,6 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public List<UserInfo> getUsers() { - enforceSystemOrRoot("Only the system can query users"); - return sUserManager.getUsers(); - } - - @Override - public UserInfo getUser(int userId) { - enforceSystemOrRoot("Only the system can remove users"); - return sUserManager.getUser(userId); - } - - @Override - public void updateUserName(int userId, String name) { - enforceSystemOrRoot("Only the system can rename users"); - sUserManager.updateUserName(userId, name); - } - - @Override public void setPermissionEnforced(String permission, boolean enforced) { mContext.enforceCallingOrSelfPermission(GRANT_REVOKE_PERMISSIONS, null); if (READ_EXTERNAL_STORAGE.equals(permission)) { diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java index 56f2166..6d31f0e 100644 --- a/services/java/com/android/server/pm/PackageSettingBase.java +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -20,11 +20,13 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import android.content.pm.PackageUserState; +import android.content.pm.UserInfo; import android.util.SparseArray; -import android.util.SparseIntArray; import java.io.File; import java.util.HashSet; +import java.util.List; /** * Settings base class for pending and resolved classes. @@ -62,19 +64,11 @@ class PackageSettingBase extends GrantedPermissions { boolean permissionsFixed; boolean haveGids; + private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState(); + // Whether this package is currently stopped, thus can not be // started until explicitly launched by the user. - private SparseArray<Boolean> stopped = new SparseArray<Boolean>(); - - // Set to true if we have never launched this app. - private SparseArray<Boolean> notLaunched = new SparseArray<Boolean>(); - - /* Explicitly disabled components */ - private SparseArray<HashSet<String>> disabledComponents = new SparseArray<HashSet<String>>(); - /* Explicitly enabled components */ - private SparseArray<HashSet<String>> enabledComponents = new SparseArray<HashSet<String>>(); - /* Enabled state */ - private SparseIntArray enabled = new SparseIntArray(); + private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>(); int installStatus = PKG_INSTALL_COMPLETE; @@ -115,12 +109,11 @@ class PackageSettingBase extends GrantedPermissions { permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; - notLaunched = base.notLaunched; - - disabledComponents = (SparseArray<HashSet<String>>) base.disabledComponents.clone(); - enabledComponents = (SparseArray<HashSet<String>>) base.enabledComponents.clone(); - enabled = (SparseIntArray) base.enabled.clone(); - stopped = (SparseArray<Boolean>) base.stopped.clone(); + userState.clear(); + for (int i=0; i<base.userState.size(); i++) { + userState.put(base.userState.keyAt(i), + new PackageUserState(base.userState.valueAt(i))); + } installStatus = base.installStatus; origPackage = base.origPackage; @@ -171,103 +164,174 @@ class PackageSettingBase extends GrantedPermissions { signatures = base.signatures; permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; - stopped = base.stopped; - notLaunched = base.notLaunched; - disabledComponents = base.disabledComponents; - enabledComponents = base.enabledComponents; - enabled = base.enabled; + userState.clear(); + for (int i=0; i<base.userState.size(); i++) { + userState.put(base.userState.keyAt(i), base.userState.valueAt(i)); + } installStatus = base.installStatus; } + private PackageUserState modifyUserState(int userId) { + PackageUserState state = userState.get(userId); + if (state == null) { + state = new PackageUserState(); + userState.put(userId, state); + } + return state; + } + + public PackageUserState readUserState(int userId) { + PackageUserState state = userState.get(userId); + return state != null ? state : DEFAULT_USER_STATE; + } + void setEnabled(int state, int userId) { - enabled.put(userId, state); + modifyUserState(userId).enabled = state; } int getEnabled(int userId) { - return enabled.get(userId, COMPONENT_ENABLED_STATE_DEFAULT); + return readUserState(userId).enabled; + } + + void setInstalled(boolean inst, int userId) { + modifyUserState(userId).installed = inst; + } + + boolean getInstalled(int userId) { + return readUserState(userId).installed; + } + + boolean isAnyInstalled(int[] users) { + for (int user: users) { + if (readUserState(user).installed) { + return true; + } + } + return false; + } + + int[] getInstalledUsers(int[] users) { + int num = 0; + for (int user : users) { + if (getInstalled(user)) { + num++; + } + } + int[] res = new int[num]; + num = 0; + for (int user : users) { + if (getInstalled(user)) { + res[num] = user; + num++; + } + } + return res; } boolean getStopped(int userId) { - return stopped.get(userId, false); + return readUserState(userId).stopped; } void setStopped(boolean stop, int userId) { - stopped.put(userId, stop); + modifyUserState(userId).stopped = stop; } boolean getNotLaunched(int userId) { - return notLaunched.get(userId, false); + return readUserState(userId).notLaunched; } void setNotLaunched(boolean stop, int userId) { - notLaunched.put(userId, stop); + modifyUserState(userId).notLaunched = stop; + } + + void setUserState(int userId, int enabled, boolean installed, boolean stopped, + boolean notLaunched, HashSet<String> enabledComponents, + HashSet<String> disabledComponents) { + PackageUserState state = modifyUserState(userId); + state.enabled = enabled; + state.installed = installed; + state.stopped = stopped; + state.notLaunched = notLaunched; + state.enabledComponents = enabledComponents; + state.disabledComponents = disabledComponents; } HashSet<String> getEnabledComponents(int userId) { - return getComponentHashSet(enabledComponents, userId); + return readUserState(userId).enabledComponents; } HashSet<String> getDisabledComponents(int userId) { - return getComponentHashSet(disabledComponents, userId); + return readUserState(userId).disabledComponents; } void setEnabledComponents(HashSet<String> components, int userId) { - enabledComponents.put(userId, components); + modifyUserState(userId).enabledComponents = components; } void setDisabledComponents(HashSet<String> components, int userId) { - disabledComponents.put(userId, components); + modifyUserState(userId).disabledComponents = components; + } + + void setEnabledComponentsCopy(HashSet<String> components, int userId) { + modifyUserState(userId).enabledComponents = components != null + ? new HashSet<String>(components) : null; } - private HashSet<String> getComponentHashSet(SparseArray<HashSet<String>> setArray, int userId) { - HashSet<String> set = setArray.get(userId); - if (set == null) { - set = new HashSet<String>(1); - setArray.put(userId, set); + void setDisabledComponentsCopy(HashSet<String> components, int userId) { + modifyUserState(userId).disabledComponents = components != null + ? new HashSet<String>(components) : null; + } + + PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) { + PackageUserState state = modifyUserState(userId); + if (disabled && state.disabledComponents == null) { + state.disabledComponents = new HashSet<String>(1); + } + if (enabled && state.enabledComponents == null) { + state.enabledComponents = new HashSet<String>(1); } - return set; + return state; } void addDisabledComponent(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - disabled.add(componentClassName); + modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName); } void addEnabledComponent(String componentClassName, int userId) { - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - enabled.add(componentClassName); + modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName); } boolean enableComponentLPw(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - boolean changed = disabled.remove(componentClassName); - changed |= enabled.add(componentClassName); + PackageUserState state = modifyUserStateComponents(userId, false, true); + boolean changed = state.disabledComponents != null + ? state.disabledComponents.remove(componentClassName) : false; + changed |= state.enabledComponents.add(componentClassName); return changed; } boolean disableComponentLPw(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - boolean changed = enabled.remove(componentClassName); - changed |= disabled.add(componentClassName); + PackageUserState state = modifyUserStateComponents(userId, true, false); + boolean changed = state.enabledComponents != null + ? state.enabledComponents.remove(componentClassName) : false; + changed |= state.disabledComponents.add(componentClassName); return changed; } boolean restoreComponentLPw(String componentClassName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - boolean changed = enabled.remove(componentClassName); - changed |= disabled.remove(componentClassName); + PackageUserState state = modifyUserStateComponents(userId, true, true); + boolean changed = state.disabledComponents != null + ? state.disabledComponents.remove(componentClassName) : false; + changed |= state.enabledComponents != null + ? state.enabledComponents.remove(componentClassName) : false; return changed; } int getCurrentEnabledStateLPr(String componentName, int userId) { - HashSet<String> disabled = getComponentHashSet(disabledComponents, userId); - HashSet<String> enabled = getComponentHashSet(enabledComponents, userId); - if (enabled.contains(componentName)) { + PackageUserState state = readUserState(userId); + if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) { return COMPONENT_ENABLED_STATE_ENABLED; - } else if (disabled.contains(componentName)) { + } else if (state.disabledComponents != null + && state.disabledComponents.contains(componentName)) { return COMPONENT_ENABLED_STATE_DISABLED; } else { return COMPONENT_ENABLED_STATE_DEFAULT; @@ -275,11 +339,6 @@ class PackageSettingBase extends GrantedPermissions { } void removeUser(int userId) { - enabled.delete(userId); - stopped.delete(userId); - enabledComponents.delete(userId); - disabledComponents.delete(userId); - notLaunched.delete(userId); + userState.delete(userId); } - } diff --git a/services/java/com/android/server/pm/PackageVerificationState.java b/services/java/com/android/server/pm/PackageVerificationState.java index e5b89c1..3214e88 100644 --- a/services/java/com/android/server/pm/PackageVerificationState.java +++ b/services/java/com/android/server/pm/PackageVerificationState.java @@ -43,6 +43,8 @@ class PackageVerificationState { private boolean mRequiredVerificationPassed; + private boolean mExtendedTimeout; + /** * Create a new package verification state where {@code requiredVerifierUid} * is the user ID for the package that must reply affirmative before things @@ -55,6 +57,7 @@ class PackageVerificationState { mRequiredVerifierUid = requiredVerifierUid; mArgs = args; mSufficientVerifierUids = new SparseBooleanArray(); + mExtendedTimeout = false; } public InstallArgs getInstallArgs() { @@ -146,4 +149,22 @@ class PackageVerificationState { return true; } + + /** + * Extend the timeout for this Package to be verified. + */ + public void extendTimeout() { + if (!mExtendedTimeout) { + mExtendedTimeout = true; + } + } + + /** + * Returns whether the timeout was extended for verification. + * + * @return {@code true} if a timeout was already extended. + */ + public boolean timeoutExtended() { + return mExtendedTimeout; + } } diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java index b100eb1..5539e84 100644 --- a/services/java/com/android/server/pm/PreferredActivity.java +++ b/services/java/com/android/server/pm/PreferredActivity.java @@ -33,22 +33,38 @@ class PreferredActivity extends IntentFilter implements PreferredComponent.Callb private static final String TAG = "PreferredActivity"; private static final boolean DEBUG_FILTERS = false; + 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); + super.writeToXml(serializer); serializer.endTag(null, "filter"); } diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index 120b650..68b594a 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -34,21 +34,24 @@ import org.xmlpull.v1.XmlSerializer; import android.app.AppGlobals; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; +import android.content.pm.PackageCleanItem; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; import android.content.pm.Signature; import android.content.pm.UserInfo; +import android.content.pm.PackageUserState; import android.content.pm.VerifierDeviceIdentity; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; import android.os.Process; import android.os.RemoteException; -import android.os.UserId; +import android.os.UserHandle; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -79,6 +82,7 @@ final class Settings { private static final String TAG = "PackageSettings"; private static final boolean DEBUG_STOPPED = false; + private static final boolean DEBUG_MU = false; private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage"; private static final String ATTR_ENFORCEMENT = "enforcement"; @@ -90,9 +94,12 @@ final class Settings { private static final String TAG_PACKAGE = "pkg"; private static final String ATTR_NAME = "name"; + private static final String ATTR_USER = "user"; + private static final String ATTR_CODE = "code"; private static final String ATTR_NOT_LAUNCHED = "nl"; private static final String ATTR_ENABLED = "enabled"; private static final String ATTR_STOPPED = "stopped"; + private static final String ATTR_INSTALLED = "inst"; private final File mSettingsFilename; private final File mBackupSettingsFilename; @@ -121,6 +128,10 @@ final class Settings { 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(); } @@ -150,7 +161,8 @@ final class Settings { // Packages that have been uninstalled and still need their external // storage data deleted. - final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>(); + final SparseArray<ArrayList<PackageCleanItem>> mPackagesToBeCleaned + = new SparseArray<ArrayList<PackageCleanItem>>(); // Packages that have been renamed since they were first installed. // Keys are the new names of the packages, values are the original @@ -169,12 +181,15 @@ final class Settings { */ private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>(); + private final Context mContext; + private final File mSystemDir; - Settings() { - this(Environment.getDataDirectory()); + Settings(Context context) { + this(context, Environment.getDataDirectory()); } - Settings(File dataDir) { + Settings(Context context, File dataDir) { + mContext = context; mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs(); FileUtils.setPermissions(mSystemDir.toString(), @@ -191,10 +206,11 @@ final class Settings { PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) { + String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) { final String name = pkg.packageName; PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath, - resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add); + resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, + user, add); return p; } @@ -355,7 +371,8 @@ final class Settings { private PackageSetting getPackageLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) { + String nativeLibraryPathString, int vc, int pkgFlags, + UserHandle installUser, boolean add) { PackageSetting p = mPackages.get(name); if (p != null) { if (!p.codePath.equals(codePath)) { @@ -398,11 +415,6 @@ final class Settings { } } if (p == null) { - // Create a new PackageSettings entry. this can end up here because - // of code path mismatch or user id mismatch of an updated system partition - if (!create) { - return null; - } if (origPackage != null) { // We are consuming the data from an existing package. p = new PackageSetting(origPackage.name, name, codePath, resourcePath, @@ -436,8 +448,20 @@ final class Settings { List<UserInfo> users = getAllUsers(); if (users != null) { for (UserInfo user : users) { - p.setStopped(true, user.id); - p.setNotLaunched(true, user.id); + // By default we consider this app to be installed + // for the user if no user has been specified (which + // means to leave it at its original value, and the + // original default value is true), or we are being + // asked to install for all users, or this is the + // user we are installing for. + final boolean installed = installUser == null + || installUser.getIdentifier() == UserHandle.USER_ALL + || installUser.getIdentifier() == user.id; + p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT, + installed, + true, // stopped, + true, // notLaunched + null, null); writePackageRestrictionsLPr(user.id); } } @@ -463,12 +487,10 @@ final class Settings { if (users != null) { for (UserInfo user : users) { int userId = user.id; - p.setDisabledComponents( - new HashSet<String>(dis.getDisabledComponents(userId)), - userId); - p.setEnabledComponents( - new HashSet<String>(dis.getEnabledComponents(userId)), - userId); + p.setDisabledComponentsCopy( + dis.getDisabledComponents(userId), userId); + p.setEnabledComponentsCopy( + dis.getEnabledComponents(userId), userId); } } // Add new setting to list of user ids @@ -489,6 +511,25 @@ final class Settings { // user preferences addPackageSettingLPw(p, name, sharedUser); } + } else { + if (installUser != null) { + // The caller has explicitly specified the user they want this + // package installed for, and the package already exists. + // Make sure it conforms to the new request. + List<UserInfo> users = getAllUsers(); + if (users != null) { + for (UserInfo user : users) { + if (installUser.getIdentifier() == UserHandle.USER_ALL + || installUser.getIdentifier() == user.id) { + boolean installed = p.getInstalled(user.id); + if (!installed) { + p.setInstalled(true, user.id); + writePackageRestrictionsLPr(user.id); + } + } + } + } + } } return p; } @@ -735,6 +776,9 @@ final class Settings { } void readPackageRestrictionsLPr(int userId) { + if (DEBUG_MU) { + Log.i(TAG, "Reading package restrictions for user=" + userId); + } FileInputStream str = null; File userPackagesStateFile = getUserPackagesStateFile(userId); File backupFile = getUserPackagesStateBackupFile(userId); @@ -766,10 +810,14 @@ final class Settings { + "assuming all started"); // At first boot, make sure no packages are stopped. // We usually want to have third party apps initialize - // in the stopped state, but not at first boot. + // in the stopped state, but not at first boot. Also + // consider all applications to be installed. for (PackageSetting pkg : mPackages.values()) { - pkg.setStopped(false, userId); - pkg.setNotLaunched(false, userId); + pkg.setUserState(userId, COMPONENT_ENABLED_STATE_DEFAULT, + true, // installed + false, // stopped + false, // notLaunched + null, null); } return; } @@ -811,17 +859,21 @@ final class Settings { XmlUtils.skipCurrentTag(parser); continue; } - String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); - int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT - : Integer.parseInt(enabledStr); - ps.setEnabled(enabled, userId); - String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED); - boolean stopped = stoppedStr == null ? false : Boolean.parseBoolean(stoppedStr); - ps.setStopped(stopped, userId); - String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED); - boolean notLaunched = stoppedStr == null ? false - : Boolean.parseBoolean(notLaunchedStr); - ps.setNotLaunched(notLaunched, userId); + final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); + final int enabled = enabledStr == null + ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr); + final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED); + final boolean installed = installedStr == null + ? true : Boolean.parseBoolean(installedStr); + final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED); + final boolean stopped = stoppedStr == null + ? false : Boolean.parseBoolean(stoppedStr); + final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED); + final boolean notLaunched = stoppedStr == null + ? false : Boolean.parseBoolean(notLaunchedStr); + + HashSet<String> enabledComponents = null; + HashSet<String> disabledComponents = null; int packageDepth = parser.getDepth(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -833,13 +885,14 @@ final class Settings { } tagName = parser.getName(); if (tagName.equals(TAG_ENABLED_COMPONENTS)) { - HashSet<String> components = readComponentsLPr(parser); - ps.setEnabledComponents(components, userId); + enabledComponents = readComponentsLPr(parser); } else if (tagName.equals(TAG_DISABLED_COMPONENTS)) { - HashSet<String> components = readComponentsLPr(parser); - ps.setDisabledComponents(components, userId); + disabledComponents = readComponentsLPr(parser); } } + + ps.setUserState(userId, enabled, installed, stopped, notLaunched, + enabledComponents, disabledComponents); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: " + parser.getName()); @@ -864,7 +917,7 @@ final class Settings { private HashSet<String> readComponentsLPr(XmlPullParser parser) throws IOException, XmlPullParserException { - HashSet<String> components = new HashSet<String>(); + HashSet<String> components = null; int type; int outerDepth = parser.getDepth(); String tagName; @@ -879,6 +932,9 @@ final class Settings { if (tagName.equals(TAG_ITEM)) { String componentName = parser.getAttributeValue(null, ATTR_NAME); if (componentName != null) { + if (components == null) { + components = new HashSet<String>(); + } components.add(componentName); } } @@ -887,6 +943,9 @@ final class Settings { } void writePackageRestrictionsLPr(int userId) { + if (DEBUG_MU) { + Log.i(TAG, "Writing package restrictions for user=" + userId); + } // Keep the old stopped packages around until we know the new ones have // been successfully written. File userPackagesStateFile = getUserPackagesStateFile(userId); @@ -921,40 +980,44 @@ final class Settings { serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS); for (final PackageSetting pkg : mPackages.values()) { - if (pkg.getStopped(userId) - || pkg.getNotLaunched(userId) - || pkg.getEnabled(userId) != COMPONENT_ENABLED_STATE_DEFAULT - || pkg.getEnabledComponents(userId).size() > 0 - || pkg.getDisabledComponents(userId).size() > 0) { + PackageUserState ustate = pkg.readUserState(userId); + if (ustate.stopped || ustate.notLaunched || !ustate.installed + || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT + || (ustate.enabledComponents != null + && ustate.enabledComponents.size() > 0) + || (ustate.disabledComponents != null + && ustate.disabledComponents.size() > 0)) { serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, pkg.name); - boolean stopped = pkg.getStopped(userId); - boolean notLaunched = pkg.getNotLaunched(userId); - int enabled = pkg.getEnabled(userId); - HashSet<String> enabledComponents = pkg.getEnabledComponents(userId); - HashSet<String> disabledComponents = pkg.getDisabledComponents(userId); + if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled); - if (stopped) { + if (!ustate.installed) { + serializer.attribute(null, ATTR_INSTALLED, "false"); + } + if (ustate.stopped) { serializer.attribute(null, ATTR_STOPPED, "true"); } - if (notLaunched) { + if (ustate.notLaunched) { serializer.attribute(null, ATTR_NOT_LAUNCHED, "true"); } - if (enabled != COMPONENT_ENABLED_STATE_DEFAULT) { - serializer.attribute(null, ATTR_ENABLED, Integer.toString(enabled)); + if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { + serializer.attribute(null, ATTR_ENABLED, + Integer.toString(ustate.enabled)); } - if (enabledComponents.size() > 0) { + if (ustate.enabledComponents != null + && ustate.enabledComponents.size() > 0) { serializer.startTag(null, TAG_ENABLED_COMPONENTS); - for (final String name : enabledComponents) { + for (final String name : ustate.enabledComponents) { serializer.startTag(null, TAG_ITEM); serializer.attribute(null, ATTR_NAME, name); serializer.endTag(null, TAG_ITEM); } serializer.endTag(null, TAG_ENABLED_COMPONENTS); } - if (disabledComponents.size() > 0) { + if (ustate.disabledComponents != null + && ustate.disabledComponents.size() > 0) { serializer.startTag(null, TAG_DISABLED_COMPONENTS); - for (final String name : disabledComponents) { + for (final String name : ustate.disabledComponents) { serializer.startTag(null, TAG_ITEM); serializer.attribute(null, ATTR_NAME, name); serializer.endTag(null, TAG_ITEM); @@ -1194,9 +1257,17 @@ final class Settings { if (mPackagesToBeCleaned.size() > 0) { for (int i=0; i<mPackagesToBeCleaned.size(); i++) { - serializer.startTag(null, "cleaning-package"); - serializer.attribute(null, ATTR_NAME, mPackagesToBeCleaned.get(i)); - serializer.endTag(null, "cleaning-package"); + final int userId = mPackagesToBeCleaned.keyAt(i); + final String userStr = Integer.toString(userId); + final ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.valueAt(i); + for (int j=0; j<pkgs.size(); j++) { + serializer.startTag(null, "cleaning-package"); + PackageCleanItem item = pkgs.get(i); + serializer.attribute(null, ATTR_NAME, item.packageName); + serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false"); + serializer.attribute(null, ATTR_USER, userStr); + serializer.endTag(null, "cleaning-package"); + } } } @@ -1452,6 +1523,17 @@ final class Settings { return ret; } + void addPackageToCleanLPw(int userId, PackageCleanItem pkg) { + ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.get(userId); + if (pkgs == null) { + pkgs = new ArrayList<PackageCleanItem>(); + mPackagesToBeCleaned.put(userId, pkgs); + } + if (!pkgs.contains(pkg)) { + pkgs.add(pkg); + } + } + boolean readLPw(List<UserInfo> users) { FileInputStream str = null; if (mBackupSettingsFilename.exists()) { @@ -1529,8 +1611,21 @@ final class Settings { readDisabledSysPackageLPw(parser); } else if (tagName.equals("cleaning-package")) { String name = parser.getAttributeValue(null, ATTR_NAME); + String userStr = parser.getAttributeValue(null, ATTR_USER); + String codeStr = parser.getAttributeValue(null, ATTR_CODE); if (name != null) { - mPackagesToBeCleaned.add(name); + int user = 0; + boolean andCode = true; + try { + if (userStr != null) { + user = Integer.parseInt(userStr); + } + } catch (NumberFormatException e) { + } + if (codeStr != null) { + andCode = Boolean.parseBoolean(codeStr); + } + addPackageToCleanLPw(user, new PackageCleanItem(name, andCode)); } } else if (tagName.equals("renamed-package")) { String nname = parser.getAttributeValue(null, "new"); @@ -1580,7 +1675,24 @@ final class Settings { mReadMessages.append("Error reading: " + e.toString()); PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e); + } + if (mBackupStoppedPackagesFilename.exists() + || mStoppedPackagesFilename.exists()) { + // Read old file + readStoppedLPw(); + mBackupStoppedPackagesFilename.delete(); + mStoppedPackagesFilename.delete(); + // Migrate to new file format + writePackageRestrictionsLPr(0); + } else { + if (users == null) { + readPackageRestrictionsLPr(0); + } else { + for (UserInfo user : users) { + readPackageRestrictionsLPr(user.id); + } + } } final int N = mPendingPackages.size(); @@ -1590,7 +1702,8 @@ final class Settings { if (idObj != null && idObj instanceof SharedUserSetting) { PackageSetting p = getPackageLPw(pp.name, null, pp.realName, (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, - pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true); + pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, + UserHandle.ALL, true); if (p == null) { PackageManagerService.reportSettingsProblem(Log.WARN, "Unable to create application package for " + pp.name); @@ -1624,23 +1737,6 @@ final class Settings { } } - if (mBackupStoppedPackagesFilename.exists() - || mStoppedPackagesFilename.exists()) { - // Read old file - readStoppedLPw(); - mBackupStoppedPackagesFilename.delete(); - mStoppedPackagesFilename.delete(); - // Migrate to new file format - writePackageRestrictionsLPr(0); - } else { - if (users == null) { - readPackageRestrictionsLPr(0); - } else { - for (UserInfo user : users) { - readPackageRestrictionsLPr(user.id); - } - } - } mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, " + mSharedUsers.size() + " shared uids\n"); @@ -2276,6 +2372,10 @@ final class Settings { return ps; } + private String compToString(HashSet<String> cmp) { + return cmp != null ? Arrays.toString(cmp.toArray()) : "[]"; + } + boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) { if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { return true; @@ -2286,24 +2386,26 @@ final class Settings { Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = " + componentInfo.packageName + " componentName = " + componentInfo.name); Log.v(PackageManagerService.TAG, "enabledComponents: " - + Arrays.toString(packageSettings.getEnabledComponents(userId).toArray())); + + compToString(packageSettings.getEnabledComponents(userId))); Log.v(PackageManagerService.TAG, "disabledComponents: " - + Arrays.toString(packageSettings.getDisabledComponents(userId).toArray())); + + compToString(packageSettings.getDisabledComponents(userId))); } if (packageSettings == null) { return false; } - final int enabled = packageSettings.getEnabled(userId); - if (enabled == COMPONENT_ENABLED_STATE_DISABLED - || enabled == COMPONENT_ENABLED_STATE_DISABLED_USER + PackageUserState ustate = packageSettings.readUserState(userId); + if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED + || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled - && enabled == COMPONENT_ENABLED_STATE_DEFAULT)) { + && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) { return false; } - if (packageSettings.getEnabledComponents(userId).contains(componentInfo.name)) { + if (ustate.enabledComponents != null + && ustate.enabledComponents.contains(componentInfo.name)) { return true; } - if (packageSettings.getDisabledComponents(userId).contains(componentInfo.name)) { + if (ustate.disabledComponents != null + && ustate.disabledComponents.contains(componentInfo.name)) { return false; } return componentInfo.enabled; @@ -2337,7 +2439,7 @@ final class Settings { boolean setPackageStoppedStateLPw(String packageName, boolean stopped, boolean allowedByPermission, int uid, int userId) { - int appId = UserId.getAppId(uid); + int appId = UserHandle.getAppId(uid); final PackageSetting pkgSetting = mPackages.get(packageName); if (pkgSetting == null) { throw new IllegalArgumentException("Unknown package: " + packageName); @@ -2362,7 +2464,7 @@ final class Settings { if (pkgSetting.installerPackageName != null) { PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgSetting.name, null, - pkgSetting.installerPackageName, null, userId); + pkgSetting.installerPackageName, null, new int[] {userId}); } pkgSetting.setNotLaunched(false, userId); } @@ -2374,9 +2476,7 @@ final class Settings { private List<UserInfo> getAllUsers() { long id = Binder.clearCallingIdentity(); try { - return AppGlobals.getPackageManager().getUsers(); - } catch (RemoteException re) { - // Local to system process, shouldn't happen + return UserManagerService.getInstance().getUsers(); } catch (NullPointerException npe) { // packagemanager not yet initialized } finally { @@ -2413,7 +2513,6 @@ final class Settings { ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION", ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE", ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP", - ApplicationInfo.FLAG_STOPPED, "STOPPED", ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK", ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE", }; @@ -2523,22 +2622,28 @@ final class Settings { pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); pw.print(" haveGids="); pw.println(ps.haveGids); pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); - pw.print(" installStatus="); pw.print(ps.installStatus); + pw.print(" installStatus="); pw.println(ps.installStatus); for (UserInfo user : users) { - pw.print(" User "); pw.print(user.id); pw.print(": "); + pw.print(" User "); pw.print(user.id); pw.print(": "); + pw.print(" installed="); + pw.print(ps.getInstalled(user.id)); pw.print(" stopped="); pw.print(ps.getStopped(user.id)); + pw.print(" notLaunched="); + pw.print(ps.getNotLaunched(user.id)); pw.print(" enabled="); pw.println(ps.getEnabled(user.id)); - if (ps.getDisabledComponents(user.id).size() > 0) { - pw.println(" disabledComponents:"); - for (String s : ps.getDisabledComponents(user.id)) { + HashSet<String> cmp = ps.getDisabledComponents(user.id); + if (cmp != null && cmp.size() > 0) { + pw.println(" disabledComponents:"); + for (String s : cmp) { pw.print(" "); pw.println(s); } } - if (ps.getEnabledComponents(user.id).size() > 0) { - pw.println(" enabledComponents:"); - for (String s : ps.getEnabledComponents(user.id)) { + cmp = ps.getEnabledComponents(user.id); + if (cmp != null && cmp.size() > 0) { + pw.println(" enabledComponents:"); + for (String s : cmp) { pw.print(" "); pw.println(s); } } diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java deleted file mode 100644 index 4e9e666..0000000 --- a/services/java/com/android/server/pm/UserManager.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; - -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.os.Environment; -import android.os.FileUtils; -import android.os.SystemClock; -import android.os.UserId; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; -import android.util.Xml; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -public class UserManager { - private static final String TAG_NAME = "name"; - - private static final String ATTR_FLAGS = "flags"; - - private static final String ATTR_ID = "id"; - - private static final String TAG_USERS = "users"; - - private static final String TAG_USER = "user"; - - private static final String LOG_TAG = "UserManager"; - - private static final String USER_INFO_DIR = "system" + File.separator + "users"; - private static final String USER_LIST_FILENAME = "userlist.xml"; - - private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); - - private final File mUsersDir; - private final File mUserListFile; - private int[] mUserIds; - - private Installer mInstaller; - private File mBaseUserPath; - - /** - * Available for testing purposes. - */ - UserManager(File dataDir, File baseUserPath) { - mUsersDir = new File(dataDir, USER_INFO_DIR); - mUsersDir.mkdirs(); - // Make zeroth user directory, for services to migrate their files to that location - File userZeroDir = new File(mUsersDir, "0"); - userZeroDir.mkdirs(); - mBaseUserPath = baseUserPath; - FileUtils.setPermissions(mUsersDir.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG - |FileUtils.S_IROTH|FileUtils.S_IXOTH, - -1, -1); - mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); - readUserList(); - } - - public UserManager(Installer installer, File baseUserPath) { - this(Environment.getDataDirectory(), baseUserPath); - mInstaller = installer; - } - - public List<UserInfo> getUsers() { - synchronized (mUsers) { - ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); - for (int i = 0; i < mUsers.size(); i++) { - users.add(mUsers.valueAt(i)); - } - return users; - } - } - - public UserInfo getUser(int userId) { - synchronized (mUsers) { - UserInfo info = mUsers.get(userId); - return info; - } - } - - public boolean exists(int userId) { - synchronized (mUsers) { - return ArrayUtils.contains(mUserIds, userId); - } - } - - public void updateUserName(int userId, String name) { - synchronized (mUsers) { - UserInfo info = mUsers.get(userId); - if (name != null && !name.equals(info.name)) { - info.name = name; - writeUserLocked(info); - } - } - } - - /** - * Returns an array of user ids. This array is cached here for quick access, so do not modify or - * cache it elsewhere. - * @return the array of user ids. - */ - int[] getUserIds() { - return mUserIds; - } - - private void readUserList() { - synchronized (mUsers) { - readUserListLocked(); - } - } - - private void readUserListLocked() { - if (!mUserListFile.exists()) { - fallbackToSingleUserLocked(); - return; - } - FileInputStream fis = null; - try { - fis = new FileInputStream(mUserListFile); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int type; - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - ; - } - - if (type != XmlPullParser.START_TAG) { - Slog.e(LOG_TAG, "Unable to read user list"); - fallbackToSingleUserLocked(); - return; - } - - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { - String id = parser.getAttributeValue(null, ATTR_ID); - UserInfo user = readUser(Integer.parseInt(id)); - if (user != null) { - mUsers.put(user.id, user); - } - } - } - updateUserIdsLocked(); - } catch (IOException ioe) { - fallbackToSingleUserLocked(); - } catch (XmlPullParserException pe) { - fallbackToSingleUserLocked(); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } - } - } - } - - private void fallbackToSingleUserLocked() { - // Create the primary user - UserInfo primary = new UserInfo(0, "Primary", - UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); - mUsers.put(0, primary); - updateUserIdsLocked(); - - writeUserListLocked(); - writeUserLocked(primary); - } - - /* - * Writes the user file in this format: - * - * <user flags="20039023" id="0"> - * <name>Primary</name> - * </user> - */ - private void writeUserLocked(UserInfo userInfo) { - FileOutputStream fos = null; - try { - final File mUserFile = new File(mUsersDir, userInfo.id + ".xml"); - fos = new FileOutputStream(mUserFile); - final BufferedOutputStream bos = new BufferedOutputStream(fos); - - // XmlSerializer serializer = XmlUtils.serializerInstance(); - final XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(bos, "utf-8"); - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - serializer.startTag(null, TAG_USER); - serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); - serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); - - serializer.startTag(null, TAG_NAME); - serializer.text(userInfo.name); - serializer.endTag(null, TAG_NAME); - - serializer.endTag(null, TAG_USER); - - serializer.endDocument(); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException ioe) { - } - } - } - } - - /* - * Writes the user list file in this format: - * - * <users> - * <user id="0"></user> - * <user id="2"></user> - * </users> - */ - private void writeUserListLocked() { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(mUserListFile); - final BufferedOutputStream bos = new BufferedOutputStream(fos); - - // XmlSerializer serializer = XmlUtils.serializerInstance(); - final XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(bos, "utf-8"); - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - serializer.startTag(null, TAG_USERS); - - for (int i = 0; i < mUsers.size(); i++) { - UserInfo user = mUsers.valueAt(i); - serializer.startTag(null, TAG_USER); - serializer.attribute(null, ATTR_ID, Integer.toString(user.id)); - serializer.endTag(null, TAG_USER); - } - - serializer.endTag(null, TAG_USERS); - - serializer.endDocument(); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "Error writing user list"); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException ioe) { - } - } - } - } - - private UserInfo readUser(int id) { - int flags = 0; - String name = null; - - FileInputStream fis = null; - try { - File userFile = new File(mUsersDir, Integer.toString(id) + ".xml"); - fis = new FileInputStream(userFile); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int type; - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - ; - } - - if (type != XmlPullParser.START_TAG) { - Slog.e(LOG_TAG, "Unable to read user " + id); - return null; - } - - if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { - String storedId = parser.getAttributeValue(null, ATTR_ID); - if (Integer.parseInt(storedId) != id) { - Slog.e(LOG_TAG, "User id does not match the file name"); - return null; - } - String flagString = parser.getAttributeValue(null, ATTR_FLAGS); - flags = Integer.parseInt(flagString); - - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - } - if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) { - type = parser.next(); - if (type == XmlPullParser.TEXT) { - name = parser.getText(); - } - } - } - - UserInfo userInfo = new UserInfo(id, name, flags); - return userInfo; - - } catch (IOException ioe) { - } catch (XmlPullParserException pe) { - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } - } - } - return null; - } - - public UserInfo createUser(String name, int flags) { - int userId = getNextAvailableId(); - UserInfo userInfo = new UserInfo(userId, name, flags); - File userPath = new File(mBaseUserPath, Integer.toString(userId)); - if (!createPackageFolders(userId, userPath)) { - return null; - } - synchronized (mUsers) { - mUsers.put(userId, userInfo); - writeUserListLocked(); - writeUserLocked(userInfo); - updateUserIdsLocked(); - } - return userInfo; - } - - /** - * Removes a user and all data directories created for that user. This method should be called - * after the user's processes have been terminated. - * @param id the user's id - */ - public boolean removeUser(int id) { - synchronized (mUsers) { - return removeUserLocked(id); - } - } - - private boolean removeUserLocked(int id) { - // Remove from the list - UserInfo userInfo = mUsers.get(id); - if (userInfo != null) { - // Remove this user from the list - mUsers.remove(id); - // Remove user file - File userFile = new File(mUsersDir, id + ".xml"); - userFile.delete(); - // Update the user list - writeUserListLocked(); - updateUserIdsLocked(); - return true; - } - return false; - } - - public void installPackageForAllUsers(String packageName, int uid) { - for (int userId : mUserIds) { - // Don't do it for the primary user, it will become recursive. - if (userId == 0) - continue; - mInstaller.createUserData(packageName, UserId.getUid(userId, uid), - userId); - } - } - - public void clearUserDataForAllUsers(String packageName) { - for (int userId : mUserIds) { - // Don't do it for the primary user, it will become recursive. - if (userId == 0) - continue; - mInstaller.clearUserData(packageName, userId); - } - } - - public void removePackageForAllUsers(String packageName) { - for (int userId : mUserIds) { - // Don't do it for the primary user, it will become recursive. - if (userId == 0) - continue; - mInstaller.remove(packageName, userId); - } - } - - /** - * Caches the list of user ids in an array, adjusting the array size when necessary. - */ - private void updateUserIdsLocked() { - if (mUserIds == null || mUserIds.length != mUsers.size()) { - mUserIds = new int[mUsers.size()]; - } - for (int i = 0; i < mUsers.size(); i++) { - mUserIds[i] = mUsers.keyAt(i); - } - } - - /** - * Returns the next available user id, filling in any holes in the ids. - * TODO: May not be a good idea to recycle ids, in case it results in confusion - * for data and battery stats collection, or unexpected cross-talk. - * @return - */ - private int getNextAvailableId() { - int i = 0; - while (i < Integer.MAX_VALUE) { - if (mUsers.indexOfKey(i) < 0) { - break; - } - i++; - } - return i; - } - - private boolean createPackageFolders(int id, File userPath) { - // mInstaller may not be available for unit-tests. - if (mInstaller == null) return true; - - // Create the user path - userPath.mkdir(); - FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG - | FileUtils.S_IXOTH, -1, -1); - - mInstaller.cloneUserData(0, id, false); - - return true; - } - - boolean removePackageFolders(int id) { - // mInstaller may not be available for unit-tests. - if (mInstaller == null) return true; - - mInstaller.removeUserDataDirs(id); - return true; - } -} diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java new file mode 100644 index 0000000..750aa72 --- /dev/null +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2011 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 static android.os.ParcelFileDescriptor.MODE_CREATE; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; + +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastXmlSerializer; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.Binder; +import android.os.Environment; +import android.os.FileUtils; +import android.os.IUserManager; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.UserHandle; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Xml; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +public class UserManagerService extends IUserManager.Stub { + + private static final String LOG_TAG = "UserManagerService"; + + private static final String TAG_NAME = "name"; + private static final String ATTR_FLAGS = "flags"; + private static final String ATTR_ICON_PATH = "icon"; + private static final String ATTR_ID = "id"; + private static final String ATTR_SERIAL_NO = "serialNumber"; + private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; + private static final String TAG_USERS = "users"; + private static final String TAG_USER = "user"; + + private static final String USER_INFO_DIR = "system" + File.separator + "users"; + private static final String USER_LIST_FILENAME = "userlist.xml"; + private static final String USER_PHOTO_FILENAME = "photo.png"; + + private final Context mContext; + private final PackageManagerService mPm; + private final Object mInstallLock; + private final Object mPackagesLock; + + private final File mUsersDir; + private final File mUserListFile; + private final File mBaseUserPath; + + private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); + + private int[] mUserIds; + private boolean mGuestEnabled; + private int mNextSerialNumber; + + private static UserManagerService sInstance; + + public static UserManagerService getInstance() { + synchronized (UserManagerService.class) { + return sInstance; + } + } + + /** + * Available for testing purposes. + */ + UserManagerService(File dataDir, File baseUserPath) { + this(null, null, new Object(), new Object(), dataDir, baseUserPath); + } + + /** + * Called by package manager to create the service. This is closely + * associated with the package manager, and the given lock is the + * package manager's own lock. + */ + UserManagerService(Context context, PackageManagerService pm, + Object installLock, Object packagesLock) { + this(context, pm, installLock, packagesLock, + Environment.getDataDirectory(), + new File(Environment.getDataDirectory(), "user")); + } + + /** + * Available for testing purposes. + */ + private UserManagerService(Context context, PackageManagerService pm, + Object installLock, Object packagesLock, + File dataDir, File baseUserPath) { + synchronized (UserManagerService.class) { + mContext = context; + mPm = pm; + mInstallLock = installLock; + mPackagesLock = packagesLock; + mUsersDir = new File(dataDir, USER_INFO_DIR); + mUsersDir.mkdirs(); + // Make zeroth user directory, for services to migrate their files to that location + File userZeroDir = new File(mUsersDir, "0"); + userZeroDir.mkdirs(); + mBaseUserPath = baseUserPath; + FileUtils.setPermissions(mUsersDir.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG + |FileUtils.S_IROTH|FileUtils.S_IXOTH, + -1, -1); + mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); + readUserList(); + sInstance = this; + } + } + + @Override + public List<UserInfo> getUsers() { + checkManageUsersPermission("query users"); + synchronized (mPackagesLock) { + ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); + for (int i = 0; i < mUsers.size(); i++) { + users.add(mUsers.valueAt(i)); + } + return users; + } + } + + @Override + public UserInfo getUserInfo(int userId) { + checkManageUsersPermission("query user"); + synchronized (mPackagesLock) { + return getUserInfoLocked(userId); + } + } + + /* + * Should be locked on mUsers before calling this. + */ + private UserInfo getUserInfoLocked(int userId) { + return mUsers.get(userId); + } + + public boolean exists(int userId) { + synchronized (mPackagesLock) { + return ArrayUtils.contains(mUserIds, userId); + } + } + + @Override + public void setUserName(int userId, String name) { + checkManageUsersPermission("rename users"); + synchronized (mPackagesLock) { + UserInfo info = mUsers.get(userId); + if (name != null && !name.equals(info.name)) { + info.name = name; + writeUserLocked(info); + } + } + } + + @Override + public ParcelFileDescriptor setUserIcon(int userId) { + checkManageUsersPermission("update users"); + synchronized (mPackagesLock) { + UserInfo info = mUsers.get(userId); + if (info == null) return null; + ParcelFileDescriptor fd = updateIconBitmapLocked(info); + if (fd != null) { + writeUserLocked(info); + } + return fd; + } + } + + @Override + public void setGuestEnabled(boolean enable) { + checkManageUsersPermission("enable guest users"); + synchronized (mPackagesLock) { + if (mGuestEnabled != enable) { + mGuestEnabled = enable; + // Erase any guest user that currently exists + for (int i = 0; i < mUsers.size(); i++) { + UserInfo user = mUsers.valueAt(i); + if (user.isGuest()) { + if (!enable) { + removeUser(user.id); + } + return; + } + } + // No guest was found + if (enable) { + createUser("Guest", UserInfo.FLAG_GUEST); + } + } + } + } + + @Override + public boolean isGuestEnabled() { + synchronized (mPackagesLock) { + return mGuestEnabled; + } + } + + @Override + public void wipeUser(int userHandle) { + checkManageUsersPermission("wipe user"); + // TODO: + } + + /** + * Enforces that only the system UID or root's UID or apps that have the + * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS} + * permission can make certain calls to the UserManager. + * + * @param message used as message if SecurityException is thrown + * @throws SecurityException if the caller is not system or root + */ + private static final void checkManageUsersPermission(String message) { + final int uid = Binder.getCallingUid(); + if (uid != Process.SYSTEM_UID && uid != 0 + && ActivityManager.checkComponentPermission( + android.Manifest.permission.MANAGE_USERS, + uid, -1, true) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need MANAGE_USERS permission to: " + message); + } + } + + private ParcelFileDescriptor updateIconBitmapLocked(UserInfo info) { + try { + File dir = new File(mUsersDir, Integer.toString(info.id)); + File file = new File(dir, USER_PHOTO_FILENAME); + if (!dir.exists()) { + dir.mkdir(); + FileUtils.setPermissions( + dir.getPath(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, + -1, -1); + } + ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, + MODE_CREATE|MODE_READ_WRITE); + info.iconPath = file.getAbsolutePath(); + return fd; + } catch (FileNotFoundException e) { + Slog.w(LOG_TAG, "Error setting photo for user ", e); + } + return null; + } + + /** + * Returns an array of user ids. This array is cached here for quick access, so do not modify or + * cache it elsewhere. + * @return the array of user ids. + */ + int[] getUserIds() { + synchronized (mPackagesLock) { + return mUserIds; + } + } + + int[] getUserIdsLPr() { + return mUserIds; + } + + private void readUserList() { + synchronized (mPackagesLock) { + readUserListLocked(); + } + } + + private void readUserListLocked() { + mGuestEnabled = false; + if (!mUserListFile.exists()) { + fallbackToSingleUserLocked(); + return; + } + FileInputStream fis = null; + AtomicFile userListFile = new AtomicFile(mUserListFile); + try { + fis = userListFile.openRead(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, null); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + Slog.e(LOG_TAG, "Unable to read user list"); + fallbackToSingleUserLocked(); + return; + } + + mNextSerialNumber = -1; + if (parser.getName().equals(TAG_USERS)) { + String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO); + if (lastSerialNumber != null) { + mNextSerialNumber = Integer.parseInt(lastSerialNumber); + } + } + + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { + String id = parser.getAttributeValue(null, ATTR_ID); + UserInfo user = readUser(Integer.parseInt(id)); + if (user != null) { + mUsers.put(user.id, user); + if (user.isGuest()) { + mGuestEnabled = true; + } + if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) { + mNextSerialNumber = user.id + 1; + } + } + } + } + updateUserIdsLocked(); + } catch (IOException ioe) { + fallbackToSingleUserLocked(); + } catch (XmlPullParserException pe) { + fallbackToSingleUserLocked(); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + } + } + } + } + + private void fallbackToSingleUserLocked() { + // Create the primary user + UserInfo primary = new UserInfo(0, "Primary", null, + UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); + mUsers.put(0, primary); + updateUserIdsLocked(); + + writeUserListLocked(); + writeUserLocked(primary); + } + + /* + * Writes the user file in this format: + * + * <user flags="20039023" id="0"> + * <name>Primary</name> + * </user> + */ + private void writeUserLocked(UserInfo userInfo) { + FileOutputStream fos = null; + AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml")); + try { + fos = userFile.startWrite(); + final BufferedOutputStream bos = new BufferedOutputStream(fos); + + // XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(bos, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, TAG_USER); + serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); + serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber)); + serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); + if (userInfo.iconPath != null) { + serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); + } + + serializer.startTag(null, TAG_NAME); + serializer.text(userInfo.name); + serializer.endTag(null, TAG_NAME); + + serializer.endTag(null, TAG_USER); + + serializer.endDocument(); + userFile.finishWrite(fos); + } catch (Exception ioe) { + Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe); + userFile.failWrite(fos); + } + } + + /* + * Writes the user list file in this format: + * + * <users nextSerialNumber="3"> + * <user id="0"></user> + * <user id="2"></user> + * </users> + */ + private void writeUserListLocked() { + FileOutputStream fos = null; + AtomicFile userListFile = new AtomicFile(mUserListFile); + try { + fos = userListFile.startWrite(); + final BufferedOutputStream bos = new BufferedOutputStream(fos); + + // XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(bos, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, TAG_USERS); + serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber)); + + for (int i = 0; i < mUsers.size(); i++) { + UserInfo user = mUsers.valueAt(i); + serializer.startTag(null, TAG_USER); + serializer.attribute(null, ATTR_ID, Integer.toString(user.id)); + serializer.endTag(null, TAG_USER); + } + + serializer.endTag(null, TAG_USERS); + + serializer.endDocument(); + userListFile.finishWrite(fos); + } catch (Exception e) { + userListFile.failWrite(fos); + Slog.e(LOG_TAG, "Error writing user list"); + } + } + + private UserInfo readUser(int id) { + int flags = 0; + int serialNumber = id; + String name = null; + String iconPath = null; + + FileInputStream fis = null; + try { + AtomicFile userFile = + new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml")); + fis = userFile.openRead(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, null); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + Slog.e(LOG_TAG, "Unable to read user " + id); + return null; + } + + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { + String storedId = parser.getAttributeValue(null, ATTR_ID); + if (Integer.parseInt(storedId) != id) { + Slog.e(LOG_TAG, "User id does not match the file name"); + return null; + } + String serialNumberValue = parser.getAttributeValue(null, ATTR_SERIAL_NO); + if (serialNumberValue != null) { + serialNumber = Integer.parseInt(serialNumberValue); + } + String flagString = parser.getAttributeValue(null, ATTR_FLAGS); + flags = Integer.parseInt(flagString); + iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH); + + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + name = parser.getText(); + } + } + } + + UserInfo userInfo = new UserInfo(id, name, iconPath, flags); + userInfo.serialNumber = serialNumber; + return userInfo; + + } catch (IOException ioe) { + } catch (XmlPullParserException pe) { + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + } + } + } + return null; + } + + @Override + public UserInfo createUser(String name, int flags) { + checkManageUsersPermission("Only the system can create users"); + int userId = getNextAvailableId(); + UserInfo userInfo = new UserInfo(userId, name, null, flags); + File userPath = new File(mBaseUserPath, Integer.toString(userId)); + synchronized (mInstallLock) { + synchronized (mPackagesLock) { + userInfo.serialNumber = mNextSerialNumber++; + mUsers.put(userId, userInfo); + writeUserListLocked(); + writeUserLocked(userInfo); + updateUserIdsLocked(); + mPm.createNewUserLILPw(userId, userPath); + } + } + if (userInfo != null) { + Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); + mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); + mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_BOOT_COMPLETED), + new UserHandle(userInfo.id)); + } + return userInfo; + } + + /** + * Removes a user and all data directories created for that user. This method should be called + * after the user's processes have been terminated. + * @param id the user's id + */ + public boolean removeUser(int userHandle) { + checkManageUsersPermission("Only the system can remove users"); + synchronized (mInstallLock) { + synchronized (mPackagesLock) { + final UserInfo user = mUsers.get(userHandle); + if (userHandle == 0 || user == null) { + return false; + } + + // Cleanup package manager settings + mPm.cleanUpUserLILPw(userHandle); + + // Remove this user from the list + mUsers.remove(userHandle); + // Remove user file + AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml")); + userFile.delete(); + // Update the user list + writeUserListLocked(); + updateUserIdsLocked(); + } + } + + // Let other services shutdown any activity + Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); + mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS); + return true; + } + + @Override + public int getUserSerialNumber(int userHandle) { + synchronized (mPackagesLock) { + if (!exists(userHandle)) return -1; + return getUserInfoLocked(userHandle).serialNumber; + } + } + + @Override + public int getUserHandle(int userSerialNumber) { + synchronized (mPackagesLock) { + for (int userId : mUserIds) { + if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId; + } + // Not found + return -1; + } + } + + /** + * Caches the list of user ids in an array, adjusting the array size when necessary. + */ + private void updateUserIdsLocked() { + int[] newUsers = new int[mUsers.size()]; + for (int i = 0; i < mUsers.size(); i++) { + newUsers[i] = mUsers.keyAt(i); + } + mUserIds = newUsers; + } + + /** + * Returns the next available user id, filling in any holes in the ids. + * TODO: May not be a good idea to recycle ids, in case it results in confusion + * for data and battery stats collection, or unexpected cross-talk. + * @return + */ + private int getNextAvailableId() { + synchronized (mPackagesLock) { + int i = 0; + while (i < Integer.MAX_VALUE) { + if (mUsers.indexOfKey(i) < 0) { + break; + } + i++; + } + return i; + } + } +} diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java new file mode 100644 index 0000000..cd211da --- /dev/null +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -0,0 +1,1177 @@ +/* + * 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; + +import com.android.server.LightsService; +import com.android.server.TwilightService; +import com.android.server.TwilightService.TwilightState; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.hardware.SystemSensorManager; +import android.hardware.display.DisplayManager; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.SystemClock; +import android.text.format.DateUtils; +import android.util.FloatMath; +import android.util.Slog; +import android.util.Spline; +import android.util.TimeUtils; +import android.view.Display; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; + +/** + * Controls the power state of the display. + * + * Handles the proximity sensor, light sensor, and animations between states + * including the screen off animation. + * + * This component acts independently of the rest of the power manager service. + * In particular, it does not share any state and it only communicates + * via asynchronous callbacks to inform the power manager that something has + * changed. + * + * Everything this class does internally is serialized on its handler although + * it may be accessed by other threads from the outside. + * + * Note that the power manager service guarantees that it will hold a suspend + * blocker as long as the display is not ready. So most of the work done here + * does not need to worry about holding a suspend blocker unless it happens + * independently of the display ready signal. + * + * For debugging, you can make the electron beam and brightness animations run + * slower by changing the "animator duration scale" option in Development Settings. + */ +final class DisplayPowerController { + private static final String TAG = "DisplayPowerController"; + + private static boolean DEBUG = false; + private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false; + private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false; + + // If true, uses the electron beam on animation. + // We might want to turn this off if we cannot get a guarantee that the screen + // actually turns on and starts showing new content after the call to set the + // screen state returns. Playing the animation can also be somewhat slow. + private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = false; + + // If true, enables the use of the screen auto-brightness adjustment setting. + private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = false; + + // The maximum range of gamma adjustment possible using the screen + // auto-brightness adjustment setting. + private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f; + + // 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 + // poorly at low light levels so we compensate for it by making an + // assumption about the environment. + private static final boolean USE_TWILIGHT_ADJUSTMENT = true; + + // Specifies the maximum magnitude of the time of day adjustment. + private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1.5f; + + // The amount of time after or before sunrise over which to start adjusting + // the gamma. We want the change to happen gradually so that it is below the + // threshold of perceptibility and so that the adjustment has maximum effect + // well after dusk. + private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2; + + private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 250; + private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 450; + + private static final int MSG_UPDATE_POWER_STATE = 1; + private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2; + private static final int MSG_LIGHT_SENSOR_DEBOUNCED = 3; + + private static final int PROXIMITY_UNKNOWN = -1; + 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; + + // Trigger proximity if distance is less than 5 cm. + private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; + + // Light sensor event rate in microseconds. + private static final int LIGHT_SENSOR_RATE = 1000000; + + // Brightness animation ramp rate in brightness units per second. + private static final int BRIGHTNESS_RAMP_RATE_FAST = 200; + private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40; + + // Filter time constant in milliseconds for computing a moving + // average of light samples. Different constants are used + // to calculate the average light level when adapting to brighter or + // dimmer environments. + // This parameter only controls the filtering of light samples. + private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 600; + private static final long DIMMING_LIGHT_TIME_CONSTANT = 4000; + + // Stability requirements in milliseconds for accepting a new brightness + // level. This is used for debouncing the light sensor. Different constants + // are used to debounce the light sensor when adapting to brighter or dimmer + // environments. + // This parameter controls how quickly brightness changes occur in response to + // an observed change in light level. + private static final long BRIGHTENING_LIGHT_DEBOUNCE = 2500; + private static final long DIMMING_LIGHT_DEBOUNCE = 10000; + + private final Object mLock = new Object(); + + // Notifier for sending asynchronous notifications. + private final Notifier mNotifier; + + // A suspend blocker. + private final SuspendBlocker mSuspendBlocker; + + // Our handler. + private final DisplayControllerHandler mHandler; + + // Asynchronous callbacks into the power manager service. + // Only invoked from the handler thread while no locks are held. + private final Callbacks mCallbacks; + private Handler mCallbackHandler; + + // The lights service. + private final LightsService mLights; + + // The twilight service. + private final TwilightService mTwilight; + + // The sensor manager. + private final SensorManager mSensorManager; + + // The proximity sensor, or null if not available or needed. + private Sensor mProximitySensor; + + // The light sensor, or null if not available or needed. + private Sensor mLightSensor; + + // The dim screen brightness. + private final int mScreenBrightnessDimConfig; + + // True if auto-brightness should be used. + private boolean mUseSoftwareAutoBrightnessConfig; + + // The auto-brightness spline adjustment. + // The brightness values have been scaled to a range of 0..1. + private Spline mScreenAutoBrightnessSpline; + + // Amount of time to delay auto-brightness after screen on while waiting for + // the light sensor to warm-up in milliseconds. + // May be 0 if no warm-up is required. + private int mLightSensorWarmUpTimeConfig; + + // The pending power request. + // Initially null until the first call to requestPowerState. + // Guarded by mLock. + private DisplayPowerRequest mPendingRequestLocked; + + // True if a request has been made to wait for the proximity sensor to go negative. + // Guarded by mLock. + private boolean mPendingWaitForNegativeProximityLocked; + + // True if the pending power request or wait for negative proximity flag + // has been changed since the last update occurred. + // Guarded by mLock. + private boolean mPendingRequestChangedLocked; + + // Set to true when the important parts of the pending power request have been applied. + // The important parts are mainly the screen state. Brightness changes may occur + // concurrently. + // Guarded by mLock. + private boolean mDisplayReadyLocked; + + // Set to true if a power state update is required. + // Guarded by mLock. + private boolean mPendingUpdatePowerStateLocked; + + /* The following state must only be accessed by the handler thread. */ + + // The currently requested power state. + // The power controller will progressively update its internal state to match + // the requested power state. Initially null until the first update. + private DisplayPowerRequest mPowerRequest; + + // The current power state. + // Must only be accessed on the handler thread. + private DisplayPowerState mPowerState; + + // True if the device should wait for negative proximity sensor before + // waking up the screen. This is set to false as soon as a negative + // proximity sensor measurement is observed or when the device is forced to + // go to sleep by the user. While true, the screen remains off. + private boolean mWaitingForNegativeProximity; + + // The actual proximity sensor threshold value. + private float mProximityThreshold; + + // Set to true if the proximity sensor listener has been registered + // with the sensor manager. + private boolean mProximitySensorEnabled; + + // The debounced proximity sensor state. + private int mProximity = PROXIMITY_UNKNOWN; + + // The raw non-debounced proximity sensor state. + private int mPendingProximity = PROXIMITY_UNKNOWN; + private long mPendingProximityDebounceTime; + + // True if the screen was turned off because of the proximity sensor. + // When the screen turns on again, we report user activity to the power manager. + private boolean mScreenOffBecauseOfProximity; + + // Set to true if the light sensor is enabled. + private boolean mLightSensorEnabled; + + // The time when the light sensor was enabled. + private long mLightSensorEnableTime; + + // The currently accepted average light sensor value. + private float mLightMeasurement; + + // True if the light sensor measurement is valid. + private boolean mLightMeasurementValid; + + // The number of light sensor samples that have been collected since the + // last time a light sensor reading was accepted. + private int mRecentLightSamples; + + // The moving average of recent light sensor values. + private float mRecentLightAverage; + + // True if recent light samples are getting brighter than the previous + // stable light measurement. + private boolean mRecentLightBrightening; + + // The time constant to use for filtering based on whether the + // light appears to be brightening or dimming. + private long mRecentLightTimeConstant; + + // The most recent light sample. + private float mLastLightSample; + + // The time of the most light recent sample. + private long mLastLightSampleTime; + + // The time when we accumulated the first recent light sample into mRecentLightSamples. + private long mFirstRecentLightSampleTime; + + // The upcoming debounce light sensor time. + // This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1. + private long mPendingLightSensorDebounceTime; + + // The screen brightness level that has been chosen by the auto-brightness + // algorithm. The actual brightness should ramp towards this value. + // We preserve this value even when we stop using the light sensor so + // that we can quickly revert to the previous auto-brightness level + // while the light sensor warms up. + // Use -1 if there is no current auto-brightness value available. + private int mScreenAutoBrightness = -1; + + // The last screen auto-brightness gamma. (For printing in dump() only.) + private float mLastScreenAutoBrightnessGamma = 1.0f; + + // True if the screen auto-brightness value is actually being used to + // set the display brightness. + private boolean mUsingScreenAutoBrightness; + + // Animators. + private ObjectAnimator mElectronBeamOnAnimator; + private ObjectAnimator mElectronBeamOffAnimator; + private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; + + // Twilight changed. We might recalculate auto-brightness values. + private boolean mTwilightChanged; + + /** + * Creates the display power controller. + */ + public DisplayPowerController(Looper looper, Context context, Notifier notifier, + LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker, + Callbacks callbacks, Handler callbackHandler) { + mHandler = new DisplayControllerHandler(looper); + mNotifier = notifier; + mSuspendBlocker = suspendBlocker; + mCallbacks = callbacks; + mCallbackHandler = callbackHandler; + + mLights = lights; + mTwilight = twilight; + mSensorManager = new SystemSensorManager(mHandler.getLooper()); + + final Resources resources = context.getResources(); + mScreenBrightnessDimConfig = resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessDim); + mUseSoftwareAutoBrightnessConfig = resources.getBoolean( + com.android.internal.R.bool.config_automatic_brightness_available); + if (mUseSoftwareAutoBrightnessConfig) { + int[] lux = resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLevels); + int[] screenBrightness = resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); + + mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness); + if (mScreenAutoBrightnessSpline == null) { + Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues " + + "(size " + screenBrightness.length + ") " + + "must be monotic and have exactly one more entry than " + + "config_autoBrightnessLevels (size " + lux.length + ") " + + "which must be strictly increasing. " + + "Auto-brightness will be disabled."); + mUseSoftwareAutoBrightnessConfig = false; + } + + mLightSensorWarmUpTimeConfig = resources.getInteger( + com.android.internal.R.integer.config_lightSensorWarmupTime); + } + + if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) { + mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + if (mProximitySensor != null) { + mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(), + TYPICAL_PROXIMITY_THRESHOLD); + } + } + + if (mUseSoftwareAutoBrightnessConfig + && !DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) { + mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + } + + if (mUseSoftwareAutoBrightnessConfig && USE_TWILIGHT_ADJUSTMENT) { + mTwilight.registerListener(mTwilightListener, mHandler); + } + } + + private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) { + try { + final int n = brightness.length; + float[] x = new float[n]; + float[] y = new float[n]; + y[0] = (float)brightness[0] / PowerManager.BRIGHTNESS_ON; + for (int i = 1; i < n; i++) { + x[i] = lux[i - 1]; + y[i] = (float)brightness[i] / PowerManager.BRIGHTNESS_ON; + } + + Spline spline = Spline.createMonotoneCubicSpline(x, y); + if (false) { + 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))); + } + } + return spline; + } catch (IllegalArgumentException ex) { + Slog.e(TAG, "Could not create auto-brightness spline.", ex); + return null; + } + } + + /** + * Returns true if the proximity sensor screen-off function is available. + */ + public boolean isProximitySensorAvailable() { + return mProximitySensor != null; + } + + /** + * Requests a new power state. + * The controller makes a copy of the provided object and then + * begins adjusting the power state to match what was requested. + * + * @param request The requested power state. + * @param waitForNegativeProximity If true, issues a request to wait for + * negative proximity before turning the screen back on, assuming the screen + * was turned off by the proximity sensor. + * @return True if display is ready, false if there are important changes that must + * be made asynchronously (such as turning the screen on), in which case the caller + * should grab a wake lock, watch for {@link Callbacks#onStateChanged()} then try + * the request again later until the state converges. + */ + public boolean requestPowerState(DisplayPowerRequest request, + boolean waitForNegativeProximity) { + if (DEBUG) { + Slog.d(TAG, "requestPowerState: " + + request + ", waitForNegativeProximity=" + waitForNegativeProximity); + } + + synchronized (mLock) { + boolean changed = false; + + if (waitForNegativeProximity + && !mPendingWaitForNegativeProximityLocked) { + mPendingWaitForNegativeProximityLocked = true; + changed = true; + } + + if (mPendingRequestLocked == null) { + mPendingRequestLocked = new DisplayPowerRequest(request); + changed = true; + } else if (!mPendingRequestLocked.equals(request)) { + mPendingRequestLocked.copyFrom(request); + changed = true; + } + + if (changed) { + mDisplayReadyLocked = false; + } + + if (changed && !mPendingRequestChangedLocked) { + mPendingRequestChangedLocked = true; + sendUpdatePowerStateLocked(); + } + + return mDisplayReadyLocked; + } + } + + private void sendUpdatePowerState() { + synchronized (mLock) { + sendUpdatePowerStateLocked(); + } + } + + private void sendUpdatePowerStateLocked() { + if (!mPendingUpdatePowerStateLocked) { + mPendingUpdatePowerStateLocked = true; + Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } + + private void initialize() { + final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; + Display display = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); + mPowerState = new DisplayPowerState(new ElectronBeam(display), + new PhotonicModulator(executor, + mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT), + mSuspendBlocker)); + + mElectronBeamOnAnimator = ObjectAnimator.ofFloat( + mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f); + mElectronBeamOnAnimator.setDuration(ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS); + mElectronBeamOnAnimator.addListener(mAnimatorListener); + + mElectronBeamOffAnimator = ObjectAnimator.ofFloat( + mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 1.0f, 0.0f); + mElectronBeamOffAnimator.setDuration(ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS); + mElectronBeamOffAnimator.addListener(mAnimatorListener); + + mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>( + mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS); + } + + private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + @Override + public void onAnimationEnd(Animator animation) { + sendUpdatePowerState(); + } + @Override + public void onAnimationRepeat(Animator animation) { + } + @Override + public void onAnimationCancel(Animator animation) { + } + }; + + private void updatePowerState() { + // Update the power state request. + final boolean mustNotify; + boolean mustInitialize = false; + boolean updateAutoBrightness = mTwilightChanged; + mTwilightChanged = false; + + synchronized (mLock) { + mPendingUpdatePowerStateLocked = false; + if (mPendingRequestLocked == null) { + return; // wait until first actual power request + } + + if (mPowerRequest == null) { + mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); + mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked; + mPendingWaitForNegativeProximityLocked = false; + mPendingRequestChangedLocked = false; + mustInitialize = true; + } else if (mPendingRequestChangedLocked) { + if (mPowerRequest.screenAutoBrightnessAdjustment + != mPendingRequestLocked.screenAutoBrightnessAdjustment) { + updateAutoBrightness = true; + } + mPowerRequest.copyFrom(mPendingRequestLocked); + mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; + mPendingWaitForNegativeProximityLocked = false; + mPendingRequestChangedLocked = false; + mDisplayReadyLocked = false; + } + + mustNotify = !mDisplayReadyLocked; + } + + // Initialize things the first time the power state is changed. + if (mustInitialize) { + initialize(); + } + + // Apply the proximity sensor. + if (mProximitySensor != null) { + if (mPowerRequest.useProximitySensor + && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) { + setProximitySensorEnabled(true); + if (!mScreenOffBecauseOfProximity + && mProximity == PROXIMITY_POSITIVE) { + mScreenOffBecauseOfProximity = true; + setScreenOn(false); + } + } else if (mWaitingForNegativeProximity + && mScreenOffBecauseOfProximity + && mProximity == PROXIMITY_POSITIVE + && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) { + setProximitySensorEnabled(true); + } else { + setProximitySensorEnabled(false); + mWaitingForNegativeProximity = false; + } + if (mScreenOffBecauseOfProximity + && mProximity != PROXIMITY_POSITIVE) { + mScreenOffBecauseOfProximity = false; + setScreenOn(true); + sendOnProximityNegative(); + } + } else { + mWaitingForNegativeProximity = false; + } + + // Turn on the light sensor if needed. + if (mLightSensor != null) { + setLightSensorEnabled(mPowerRequest.useAutoBrightness + && wantScreenOn(mPowerRequest.screenState), updateAutoBrightness); + } + + // 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 (mScreenAutoBrightness >= 0 && mLightSensorEnabled) { + // Use current auto-brightness value. + animateScreenBrightness( + clampScreenBrightness(mScreenAutoBrightness), + mUsingScreenAutoBrightness ? BRIGHTNESS_RAMP_RATE_SLOW : + BRIGHTNESS_RAMP_RATE_FAST); + 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); + mUsingScreenAutoBrightness = false; + } + } else { + // Screen is off. Don't bother changing the brightness. + mUsingScreenAutoBrightness = false; + } + + // Animate the screen on or off. + if (!mScreenOffBecauseOfProximity) { + if (wantScreenOn(mPowerRequest.screenState)) { + // Want screen on. + // Wait for previous off animation to complete beforehand. + // 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()) { + 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(); + } + } + } else { + mPowerState.setElectronBeamLevel(1.0f); + mPowerState.dismissElectronBeam(); + } + } + } else { + // Want screen off. + // Wait for previous on animation to complete beforehand. + if (!mElectronBeamOnAnimator.isStarted()) { + if (!mElectronBeamOffAnimator.isStarted()) { + if (mPowerState.getElectronBeamLevel() == 0.0f) { + setScreenOn(false); + } else if (mPowerState.prepareElectronBeam(false) + && mPowerState.isScreenOn()) { + mElectronBeamOffAnimator.start(); + } else { + mElectronBeamOffAnimator.end(); + } + } + } + } + } + + // Report whether the display is ready for use. + // We mostly care about the screen state here, ignoring brightness changes + // which will be handled asynchronously. + if (mustNotify + && !mElectronBeamOnAnimator.isStarted() + && !mElectronBeamOffAnimator.isStarted() + && mPowerState.waitUntilClean(mCleanListener)) { + synchronized (mLock) { + if (!mPendingRequestChangedLocked) { + mDisplayReadyLocked = true; + } + } + sendOnStateChanged(); + } + } + + private void setScreenOn(boolean on) { + if (!mPowerState.isScreenOn() == on) { + mPowerState.setScreenOn(on); + if (on) { + mNotifier.onScreenOn(); + } else { + mNotifier.onScreenOff(); + } + } + } + + private int clampScreenBrightness(int value) { + return Math.min(Math.max(Math.max(value, mScreenBrightnessDimConfig), 0), 255); + } + + private void animateScreenBrightness(int target, int rate) { + if (mScreenBrightnessRampAnimator.animateTo(target, rate)) { + mNotifier.onScreenBrightness(target); + } + } + + private final Runnable mCleanListener = new Runnable() { + @Override + public void run() { + sendUpdatePowerState(); + } + }; + + private void setProximitySensorEnabled(boolean enable) { + if (enable) { + if (!mProximitySensorEnabled) { + mProximitySensorEnabled = true; + mPendingProximity = PROXIMITY_UNKNOWN; + mSensorManager.registerListener(mProximitySensorListener, mProximitySensor, + SensorManager.SENSOR_DELAY_NORMAL, mHandler); + } + } else { + if (mProximitySensorEnabled) { + mProximitySensorEnabled = false; + mProximity = PROXIMITY_UNKNOWN; + mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); + mSensorManager.unregisterListener(mProximitySensorListener); + } + } + } + + private void handleProximitySensorEvent(long time, boolean positive) { + if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) { + return; // no change + } + if (mPendingProximity == PROXIMITY_POSITIVE && positive) { + return; // no change + } + + // 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; + debounceProximitySensor(); + } + + private void debounceProximitySensor() { + if (mPendingProximity != PROXIMITY_UNKNOWN) { + final long now = SystemClock.uptimeMillis(); + if (mPendingProximityDebounceTime <= now) { + mProximity = mPendingProximity; + sendUpdatePowerState(); + } else { + Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime); + } + } + } + + private void setLightSensorEnabled(boolean enable, boolean updateAutoBrightness) { + if (enable) { + if (!mLightSensorEnabled) { + updateAutoBrightness = true; + mLightSensorEnabled = true; + mLightSensorEnableTime = SystemClock.uptimeMillis(); + mSensorManager.registerListener(mLightSensorListener, mLightSensor, + LIGHT_SENSOR_RATE, mHandler); + } + } else { + if (mLightSensorEnabled) { + mLightSensorEnabled = false; + mLightMeasurementValid = false; + mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED); + mSensorManager.unregisterListener(mLightSensorListener); + } + } + if (updateAutoBrightness) { + updateAutoBrightness(false); + } + } + + private void handleLightSensorEvent(long time, float lux) { + // Take the first few readings during the warm-up period and apply them + // immediately without debouncing. + if (!mLightMeasurementValid + || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) { + mLightMeasurement = lux; + mLightMeasurementValid = true; + mRecentLightSamples = 0; + updateAutoBrightness(true); + } + + // Update our moving average. + if (lux != mLightMeasurement && (mRecentLightSamples == 0 + || (lux < mLightMeasurement && mRecentLightBrightening) + || (lux > mLightMeasurement && !mRecentLightBrightening))) { + // If the newest light sample doesn't seem to be going in the + // same general direction as recent samples, then start over. + setRecentLight(time, lux, lux > mLightMeasurement); + } else if (mRecentLightSamples >= 1) { + // Add the newest light sample to the moving average. + accumulateRecentLight(time, lux); + } + if (DEBUG) { + Slog.d(TAG, "handleLightSensorEvent: lux=" + lux + + ", mLightMeasurementValid=" + mLightMeasurementValid + + ", mLightMeasurement=" + mLightMeasurement + + ", mRecentLightSamples=" + mRecentLightSamples + + ", mRecentLightAverage=" + mRecentLightAverage + + ", mRecentLightBrightening=" + mRecentLightBrightening + + ", mRecentLightTimeConstant=" + mRecentLightTimeConstant + + ", mFirstRecentLightSampleTime=" + + TimeUtils.formatUptime(mFirstRecentLightSampleTime) + + ", mPendingLightSensorDebounceTime=" + + TimeUtils.formatUptime(mPendingLightSensorDebounceTime)); + } + + // Debounce. + mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED); + debounceLightSensor(); + } + + private void setRecentLight(long time, float lux, boolean brightening) { + mRecentLightBrightening = brightening; + mRecentLightTimeConstant = brightening ? + BRIGHTENING_LIGHT_TIME_CONSTANT : DIMMING_LIGHT_TIME_CONSTANT; + mRecentLightSamples = 1; + mRecentLightAverage = lux; + mLastLightSample = lux; + mLastLightSampleTime = time; + mFirstRecentLightSampleTime = time; + mPendingLightSensorDebounceTime = time + (brightening ? + BRIGHTENING_LIGHT_DEBOUNCE : DIMMING_LIGHT_DEBOUNCE); + } + + private void accumulateRecentLight(long time, float lux) { + final long timeDelta = time - mLastLightSampleTime; + mRecentLightSamples += 1; + mRecentLightAverage += (lux - mRecentLightAverage) * + timeDelta / (mRecentLightTimeConstant + timeDelta); + mLastLightSample = lux; + mLastLightSampleTime = time; + } + + private void debounceLightSensor() { + if (mLightMeasurementValid && mRecentLightSamples >= 1) { + final long now = SystemClock.uptimeMillis(); + if (mPendingLightSensorDebounceTime <= now) { + accumulateRecentLight(now, mLastLightSample); + mLightMeasurement = mRecentLightAverage; + + if (DEBUG) { + Slog.d(TAG, "debounceLightSensor: Accepted new measurement " + + mLightMeasurement + " after " + + (now - mFirstRecentLightSampleTime) + " ms based on " + + mRecentLightSamples + " recent samples."); + } + + updateAutoBrightness(true); + + // Now that we have debounced the light sensor data, we have the + // option of either leaving the sensor in a debounced state or + // restarting the debounce cycle by setting mRecentLightSamples to 0. + // + // If we leave the sensor debounced, then new average light measurements + // may be accepted immediately as long as they are trending in the same + // direction as they were before. If the measurements start + // jittering or trending in the opposite direction then the debounce + // cycle will automatically be restarted. The benefit is that the + // auto-brightness control can be more responsive to changes over a + // broad range. + // + // For now, we choose to be more responsive and leave the following line + // commented out. + // + // mRecentLightSamples = 0; + } else { + Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime); + } + } + } + + private void updateAutoBrightness(boolean sendUpdate) { + if (!mLightMeasurementValid) { + return; + } + + float value = mScreenAutoBrightnessSpline.interpolate(mLightMeasurement); + float gamma = 1.0f; + + if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT + && mPowerRequest.screenAutoBrightnessAdjustment != 0.0f) { + final float adjGamma = FloatMath.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA, + Math.min(1.0f, Math.max(-1.0f, + -mPowerRequest.screenAutoBrightnessAdjustment))); + gamma *= adjGamma; + if (DEBUG) { + Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma); + } + } + + if (USE_TWILIGHT_ADJUSTMENT) { + TwilightState state = mTwilight.getCurrentState(); + if (state != null && state.isNight()) { + final long now = System.currentTimeMillis(); + final float earlyGamma = + getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise()); + final float lateGamma = + getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise()); + gamma *= earlyGamma * lateGamma; + if (DEBUG) { + Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma + + ", lateGamma=" + lateGamma); + } + } + } + + if (gamma != 1.0f) { + final float in = value; + value = FloatMath.pow(value, gamma); + if (DEBUG) { + Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma + + ", in=" + in + ", out=" + value); + } + } + + int newScreenAutoBrightness = clampScreenBrightness( + (int)Math.round(value * PowerManager.BRIGHTNESS_ON)); + if (mScreenAutoBrightness != newScreenAutoBrightness) { + if (DEBUG) { + Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" + + mScreenAutoBrightness + ", newScreenAutoBrightness=" + + newScreenAutoBrightness); + } + + mScreenAutoBrightness = newScreenAutoBrightness; + mLastScreenAutoBrightnessGamma = gamma; + if (sendUpdate) { + sendUpdatePowerState(); + } + } + } + + private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) { + if (lastSunset < 0 || nextSunrise < 0 + || now < lastSunset || now > nextSunrise) { + return 1.0f; + } + + if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) { + return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA, + (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME); + } + + if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) { + return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA, + (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME); + } + + return TWILIGHT_ADJUSTMENT_MAX_GAMMA; + } + + private static float lerp(float x, float y, float alpha) { + return x + (y - x) * alpha; + } + + private void sendOnStateChanged() { + mCallbackHandler.post(mOnStateChangedRunnable); + } + + private final Runnable mOnStateChangedRunnable = new Runnable() { + @Override + public void run() { + mCallbacks.onStateChanged(); + } + }; + + private void sendOnProximityNegative() { + mCallbackHandler.post(mOnProximityNegativeRunnable); + } + + private final Runnable mOnProximityNegativeRunnable = new Runnable() { + @Override + public void run() { + mCallbacks.onProximityNegative(); + } + }; + + public void dump(PrintWriter pw) { + synchronized (mLock) { + pw.println(); + pw.println("Display Controller Locked State:"); + pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked); + pw.println(" mPendingRequestLocked=" + mPendingRequestLocked); + pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked); + pw.println(" mPendingWaitForNegativeProximityLocked=" + + mPendingWaitForNegativeProximityLocked); + pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked); + } + + pw.println(); + pw.println("Display Controller Configuration:"); + pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); + pw.println(" mUseSoftwareAutoBrightnessConfig=" + + mUseSoftwareAutoBrightnessConfig); + pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline); + pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig); + + if (Looper.myLooper() == mHandler.getLooper()) { + dumpLocal(pw); + } else { + final StringWriter out = new StringWriter(); + final CountDownLatch latch = new CountDownLatch(1); + Message msg = Message.obtain(mHandler, new Runnable() { + @Override + public void run() { + PrintWriter localpw = new PrintWriter(out); + try { + dumpLocal(localpw); + } finally { + localpw.flush(); + latch.countDown(); + } + } + }); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + try { + latch.await(); + pw.print(out.toString()); + } catch (InterruptedException ex) { + pw.println(); + pw.println("Failed to dump thread state due to interrupted exception!"); + } + } + } + + private void dumpLocal(PrintWriter pw) { + pw.println(); + pw.println("Display Controller Thread State:"); + pw.println(" mPowerRequest=" + mPowerRequest); + pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity); + + pw.println(" mProximitySensor=" + mProximitySensor); + pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled); + pw.println(" mProximityThreshold=" + mProximityThreshold); + pw.println(" mProximity=" + proximityToString(mProximity)); + pw.println(" mPendingProximity=" + proximityToString(mPendingProximity)); + pw.println(" mPendingProximityDebounceTime=" + + TimeUtils.formatUptime(mPendingProximityDebounceTime)); + pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity); + + pw.println(" mLightSensor=" + mLightSensor); + pw.println(" mLightSensorEnabled=" + mLightSensorEnabled); + pw.println(" mLightSensorEnableTime=" + + TimeUtils.formatUptime(mLightSensorEnableTime)); + pw.println(" mLightMeasurement=" + mLightMeasurement); + pw.println(" mLightMeasurementValid=" + mLightMeasurementValid); + pw.println(" mLastLightSample=" + mLastLightSample); + pw.println(" mLastLightSampleTime=" + + TimeUtils.formatUptime(mLastLightSampleTime)); + pw.println(" mRecentLightSamples=" + mRecentLightSamples); + pw.println(" mRecentLightAverage=" + mRecentLightAverage); + pw.println(" mRecentLightBrightening=" + mRecentLightBrightening); + pw.println(" mRecentLightTimeConstant=" + mRecentLightTimeConstant); + pw.println(" mFirstRecentLightSampleTime=" + + TimeUtils.formatUptime(mFirstRecentLightSampleTime)); + pw.println(" mPendingLightSensorDebounceTime=" + + TimeUtils.formatUptime(mPendingLightSensorDebounceTime)); + pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); + pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness); + pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma); + pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState()); + + if (mElectronBeamOnAnimator != null) { + pw.println(" mElectronBeamOnAnimator.isStarted()=" + + mElectronBeamOnAnimator.isStarted()); + } + if (mElectronBeamOffAnimator != null) { + pw.println(" mElectronBeamOffAnimator.isStarted()=" + + mElectronBeamOffAnimator.isStarted()); + } + + if (mPowerState != null) { + mPowerState.dump(pw); + } + } + + private static String proximityToString(int state) { + switch (state) { + case PROXIMITY_UNKNOWN: + return "Unknown"; + case PROXIMITY_NEGATIVE: + return "Negative"; + case PROXIMITY_POSITIVE: + return "Positive"; + default: + return Integer.toString(state); + } + } + + private static boolean wantScreenOn(int state) { + switch (state) { + case DisplayPowerRequest.SCREEN_STATE_BRIGHT: + case DisplayPowerRequest.SCREEN_STATE_DIM: + return true; + } + return false; + } + + /** + * Asynchronous callbacks from the power controller to the power manager service. + */ + public interface Callbacks { + void onStateChanged(); + void onProximityNegative(); + } + + private final class DisplayControllerHandler extends Handler { + public DisplayControllerHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_POWER_STATE: + updatePowerState(); + break; + + case MSG_PROXIMITY_SENSOR_DEBOUNCED: + debounceProximitySensor(); + break; + + case MSG_LIGHT_SENSOR_DEBOUNCED: + debounceLightSensor(); + break; + } + } + } + + private final SensorEventListener mProximitySensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + if (mProximitySensorEnabled) { + final long time = SystemClock.uptimeMillis(); + final float distance = event.values[0]; + boolean positive = distance >= 0.0f && distance < mProximityThreshold; + handleProximitySensorEvent(time, positive); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Not used. + } + }; + + private final SensorEventListener mLightSensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + if (mLightSensorEnabled) { + final long time = SystemClock.uptimeMillis(); + final float lux = event.values[0]; + handleLightSensorEvent(time, lux); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Not used. + } + }; + + private final TwilightService.TwilightListener mTwilightListener = + new TwilightService.TwilightListener() { + @Override + public void onTwilightStateChanged() { + mTwilightChanged = true; + updatePowerState(); + } + }; +} diff --git a/services/java/com/android/server/power/DisplayPowerRequest.java b/services/java/com/android/server/power/DisplayPowerRequest.java new file mode 100644 index 0000000..2d74292 --- /dev/null +++ b/services/java/com/android/server/power/DisplayPowerRequest.java @@ -0,0 +1,103 @@ +/* + * 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; + +import android.os.PowerManager; + +/** + * Describes the requested power state of the display. + * + * This object is intended to describe the general characteristics of the + * power state, such as whether the screen should be on or off and the current + * brightness controls leaving the {@link DisplayPowerController} to manage the + * details of how the transitions between states should occur. The goal is for + * the {@link PowerManagerService} to focus on the global power state and not + * have to micro-manage screen off animations, auto-brightness and other effects. + */ +final class DisplayPowerRequest { + public static final int SCREEN_STATE_OFF = 0; + public static final int SCREEN_STATE_DIM = 1; + public static final int SCREEN_STATE_BRIGHT = 2; + + // The requested minimum screen power state: off, dim or bright. + public int screenState; + + // If true, the proximity sensor overrides the screen state when an object is + // nearby, turning it off temporarily until the object is moved away. + public boolean useProximitySensor; + + // The desired screen brightness in the range 0 (minimum / off) to 255 (brightest). + // The display power controller may choose to clamp the brightness. + // When auto-brightness is enabled, this field should specify a nominal default + // value to use while waiting for the light sensor to report enough data. + public int screenBrightness; + + // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter). + public float screenAutoBrightnessAdjustment; + + // If true, enables automatic brightness control. + public boolean useAutoBrightness; + + public DisplayPowerRequest() { + screenState = SCREEN_STATE_BRIGHT; + useProximitySensor = false; + screenBrightness = PowerManager.BRIGHTNESS_ON; + screenAutoBrightnessAdjustment = 0.0f; + useAutoBrightness = false; + } + + public DisplayPowerRequest(DisplayPowerRequest other) { + copyFrom(other); + } + + public void copyFrom(DisplayPowerRequest other) { + screenState = other.screenState; + useProximitySensor = other.useProximitySensor; + screenBrightness = other.screenBrightness; + screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment; + useAutoBrightness = other.useAutoBrightness; + } + + @Override + public boolean equals(Object o) { + return o instanceof DisplayPowerRequest + && equals((DisplayPowerRequest)o); + } + + public boolean equals(DisplayPowerRequest other) { + return other != null + && screenState == other.screenState + && useProximitySensor == other.useProximitySensor + && screenBrightness == other.screenBrightness + && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment + && useAutoBrightness == other.useAutoBrightness; + } + + @Override + public int hashCode() { + return 0; // don't care + } + + @Override + public String toString() { + return "screenState=" + screenState + + ", useProximitySensor=" + useProximitySensor + + ", screenBrightness=" + screenBrightness + + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment + + ", useAutoBrightness=" + useAutoBrightness; + } +} diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java new file mode 100644 index 0000000..3524a08 --- /dev/null +++ b/services/java/com/android/server/power/DisplayPowerState.java @@ -0,0 +1,274 @@ +/* + * 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; + +import android.os.Looper; +import android.os.PowerManager; +import android.util.FloatProperty; +import android.util.IntProperty; +import android.util.Slog; +import android.view.Choreographer; + +import java.io.PrintWriter; + +/** + * Represents the current display power state and realizes it. + * + * This component is similar in nature to a {@link View} except that it describes + * the properties of a display. When properties are changed, the component + * invalidates itself and posts a callback to the {@link Choreographer} to + * apply the changes. This mechanism enables the display power state to be + * animated smoothly by the animation framework. + * + * This component must only be created or accessed by the {@link Looper} thread + * that belongs to the {@link DisplayPowerController}. + * + * We don't need to worry about holding a suspend blocker here because the + * {@link DisplayPowerController} does that for us whenever there is a pending invalidate. + */ +final class DisplayPowerState { + private static final String TAG = "DisplayPowerState"; + + private static boolean DEBUG = false; + + private static final int DIRTY_SCREEN_ON = 1 << 0; + private static final int DIRTY_ELECTRON_BEAM = 1 << 1; + private static final int DIRTY_BRIGHTNESS = 1 << 2; + + private final Choreographer mChoreographer; + private final ElectronBeam mElectronBeam; + private final PhotonicModulator mScreenBrightnessModulator; + + private int mDirty; + private boolean mScreenOn; + private float mElectronBeamLevel; + private int mScreenBrightness; + + private Runnable mCleanListener; + + public DisplayPowerState(ElectronBeam electronBean, + PhotonicModulator screenBrightnessModulator) { + mChoreographer = Choreographer.getInstance(); + mElectronBeam = electronBean; + mScreenBrightnessModulator = screenBrightnessModulator; + + // 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, + // so prepare to set it to a known state when the state is next applied. + // Although we set the brightness to full on here, the display power controller + // will reset the brightness to a new level immediately before the changes + // actually have a chance to be applied. + mScreenOn = true; + mElectronBeamLevel = 1.0f; + mScreenBrightness = PowerManager.BRIGHTNESS_ON; + invalidate(DIRTY_BRIGHTNESS); + } + + public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL = + new FloatProperty<DisplayPowerState>("electronBeamLevel") { + @Override + public void setValue(DisplayPowerState object, float value) { + object.setElectronBeamLevel(value); + } + + @Override + public Float get(DisplayPowerState object) { + return object.getElectronBeamLevel(); + } + }; + + public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS = + new IntProperty<DisplayPowerState>("screenBrightness") { + @Override + public void setValue(DisplayPowerState object, int value) { + object.setScreenBrightness(value); + } + + @Override + public Integer get(DisplayPowerState object) { + return object.getScreenBrightness(); + } + }; + + /** + * Sets whether the screen is on or off. + */ + public void setScreenOn(boolean on) { + if (mScreenOn != on) { + if (DEBUG) { + Slog.d(TAG, "setScreenOn: on=" + on); + } + + mScreenOn = on; + invalidate(DIRTY_SCREEN_ON); + } + } + + /** + * Returns true if the screen is on. + */ + public boolean isScreenOn() { + return mScreenOn; + } + + /** + * Prepares the electron beam to turn on or off. + * 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. + * @return True if the electron beam was prepared. + */ + public boolean prepareElectronBeam(boolean warmUp) { + boolean success = mElectronBeam.prepare(warmUp); + invalidate(DIRTY_ELECTRON_BEAM); + return success; + } + + /** + * Dismisses the electron beam surface. + */ + public void dismissElectronBeam() { + mElectronBeam.dismiss(); + } + + /** + * Sets the level of the electron beam steering current. + * + * The display is blanked when the level is 0.0. In normal use, the electron + * beam should have a value of 1.0. The electron beam is unstable in between + * these states and the picture quality may be compromised. For best effect, + * the electron beam should be warmed up or cooled off slowly. + * + * Warning: Electron beam emits harmful radiation. Avoid direct exposure to + * skin or eyes. + * + * @param level The level, ranges from 0.0 (full off) to 1.0 (full on). + */ + public void setElectronBeamLevel(float level) { + if (mElectronBeamLevel != level) { + if (DEBUG) { + Slog.d(TAG, "setElectronBeamLevel: level=" + level); + } + + mElectronBeamLevel = level; + invalidate(DIRTY_ELECTRON_BEAM); + } + } + + /** + * Gets the level of the electron beam steering current. + */ + public float getElectronBeamLevel() { + return mElectronBeamLevel; + } + + /** + * Sets the display brightness. + * + * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). + */ + public void setScreenBrightness(int brightness) { + if (mScreenBrightness != brightness) { + if (DEBUG) { + Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); + } + + mScreenBrightness = brightness; + invalidate(DIRTY_BRIGHTNESS); + } + } + + /** + * Gets the screen brightness. + */ + public int getScreenBrightness() { + return mScreenBrightness; + } + + /** + * Returns true if no properties have been invalidated. + * Otherwise, returns false and promises to invoke the specified listener + * when the properties have all been applied. + * The listener always overrides any previously set listener. + */ + public boolean waitUntilClean(Runnable listener) { + if (mDirty != 0) { + mCleanListener = listener; + return false; + } else { + mCleanListener = null; + return true; + } + } + + public void dump(PrintWriter pw) { + pw.println(); + pw.println("Display Power State:"); + pw.println(" mDirty=" + Integer.toHexString(mDirty)); + pw.println(" mScreenOn=" + mScreenOn); + pw.println(" mScreenBrightness=" + mScreenBrightness); + pw.println(" mElectronBeamLevel=" + mElectronBeamLevel); + + mElectronBeam.dump(pw); + } + + private void invalidate(int dirty) { + if (mDirty == 0) { + mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, + mTraversalRunnable, null); + } + + mDirty |= dirty; + } + + private void apply() { + if (mDirty != 0) { + if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) { + PowerManagerService.nativeSetScreenState(false); + } + + if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) { + mElectronBeam.draw(mElectronBeamLevel); + } + + if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0) { + mScreenBrightnessModulator.setBrightness(mScreenOn ? + (int)(mScreenBrightness * mElectronBeamLevel) : 0); + } + + if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) { + PowerManagerService.nativeSetScreenState(true); + } + + mDirty = 0; + + if (mCleanListener != null) { + mCleanListener.run(); + } + } + } + + private final Runnable mTraversalRunnable = new Runnable() { + @Override + public void run() { + if (mDirty != 0) { + apply(); + } + } + }; +} diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java new file mode 100644 index 0000000..aad5a9a --- /dev/null +++ b/services/java/com/android/server/power/ElectronBeam.java @@ -0,0 +1,652 @@ +/* + * 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; + +import android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.opengl.EGL14; +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +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; +import android.view.DisplayInfo; +import android.view.Surface; +import android.view.SurfaceSession; + +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * Bzzzoooop! *crackle* + * + * Animates a screen transition from on to off or off to on by applying + * some GL transformations to a screenshot. + * + * This component must only be created or accessed by the {@link Looper} thread + * that belongs to the {@link DisplayPowerController}. + */ +final class ElectronBeam { + private static final String TAG = "ElectronBeam"; + + private static final boolean DEBUG = false; + + // The layer for the electron beam surface. + // This is currently hardcoded to be one layer above the boot animation. + private static final int ELECTRON_BEAM_LAYER = 0x40000001; + + // The relative proportion of the animation to spend performing + // the horizontal stretch effect. The remainder is spent performing + // the vertical stretch effect. + private static final float HSTRETCH_DURATION = 0.4f; + private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION; + + // Set to true when the animation context has been fully prepared. + private boolean mPrepared; + private boolean mWarmUp; + + private final Display mDisplay; + private final DisplayInfo mDisplayInfo = new DisplayInfo(); + private int mDisplayLayerStack; // layer stack associated with primary display + private int mDisplayRotation; + private int mDisplayWidth; // real width, not rotated + private int mDisplayHeight; // real height, not rotated + private SurfaceSession mSurfaceSession; + private Surface mSurface; + private EGLDisplay mEglDisplay; + private EGLConfig mEglConfig; + private EGLContext mEglContext; + private EGLSurface mEglSurface; + private boolean mSurfaceVisible; + + // Texture names. We only use one texture, which contains the screenshot. + private final int[] mTexNames = new int[1]; + private boolean mTexNamesGenerated; + + // Vertex and corresponding texture coordinates. + // We have 4 2D vertices, so 8 elements. The vertices form a quad. + private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8); + private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8); + + public ElectronBeam(Display display) { + mDisplay = display; + } + + /** + * 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. + * @return True if the electron beam is ready, false if it is uncontrollable. + */ + public boolean prepare(boolean warmUp) { + if (DEBUG) { + Slog.d(TAG, "prepare: warmUp=" + warmUp); + } + + mWarmUp = warmUp; + + // Get the display size and adjust it for rotation. + mDisplay.getDisplayInfo(mDisplayInfo); + mDisplayLayerStack = mDisplay.getLayerStack(); + mDisplayRotation = mDisplayInfo.rotation; + if (mDisplayRotation == Surface.ROTATION_90 + || mDisplayRotation == Surface.ROTATION_270) { + mDisplayWidth = mDisplayInfo.logicalHeight; + mDisplayHeight = mDisplayInfo.logicalWidth; + } else { + mDisplayWidth = mDisplayInfo.logicalWidth; + mDisplayHeight = mDisplayInfo.logicalHeight; + } + + // Prepare the surface for drawing. + if (!createEglContext() + || !createEglSurface() + || !captureScreenshotTextureAndSetViewport()) { + dismiss(); + return false; + } + + mPrepared = true; + return true; + } + + /** + * Dismisses the electron beam animation surface and cleans up. + * + * To prevent stray photons from leaking out after the electron beam has been + * turned off, it is a good idea to defer dismissing the animation until the + * electron beam has been turned back on fully. + */ + public void dismiss() { + if (DEBUG) { + Slog.d(TAG, "dismiss"); + } + + destroyScreenshotTexture(); + destroyEglSurface(); + mPrepared = false; + } + + /** + * Draws an animation frame showing the electron beam activated at the + * specified level. + * + * @param level The electron beam level. + * @return True if successful. + */ + public boolean draw(float level) { + if (DEBUG) { + Slog.d(TAG, "drawFrame: level=" + level); + } + + if (!attachEglContext()) { + return false; + } + try { + // Clear frame to solid black. + GLES10.glClearColor(0f, 0f, 0f, 1f); + GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT); + + // Draw the frame. + if (level < HSTRETCH_DURATION) { + drawHStretch(1.0f - (level / HSTRETCH_DURATION)); + } else { + drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION)); + } + if (checkGlErrors("drawFrame")) { + return false; + } + + EGL14.eglSwapBuffers(mEglDisplay, mEglSurface); + } finally { + detachEglContext(); + } + + return showEglSurface(); + } + + /** + * Draws a frame where the content of the electron beam is collapsing inwards upon + * itself vertically with red / green / blue channels dispersing and eventually + * merging down to a single horizontal line. + * + * @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse. + */ + private void drawVStretch(float stretch) { + // compute interpolation scale factors for each color channel + final float ar = scurve(stretch, 7.5f); + final float ag = scurve(stretch, 8.0f); + final float ab = scurve(stretch, 8.5f); + if (DEBUG) { + Slog.d(TAG, "drawVStretch: stretch=" + stretch + + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab); + } + + // set blending + GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE); + GLES10.glEnable(GLES10.GL_BLEND); + + // bind vertex buffer + GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); + GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); + + // 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); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, + GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glEnable(GLES10.GL_TEXTURE_2D); + GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer); + GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + + // draw the red plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar); + GLES10.glColorMask(true, false, false, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // draw the green plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + GLES10.glColorMask(false, true, false, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // draw the blue plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab); + GLES10.glColorMask(false, false, true, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // clean up after drawing planes + GLES10.glDisable(GLES10.GL_TEXTURE_2D); + GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + GLES10.glColorMask(true, true, true, true); + + // draw the white highlight (we use the last vertices) + if (!mWarmUp) { + GLES10.glColor4f(ag, ag, ag, 1.0f); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + } + + // clean up + GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); + GLES10.glDisable(GLES10.GL_BLEND); + } + + /** + * Draws a frame where the electron beam has been stretched out into + * a thin white horizontal line that fades as it expands outwards. + * + * @param stretch The stretch factor. 0.0 is no stretch / no fade, + * 1.0 is maximum stretch / maximum fade. + */ + private void drawHStretch(float stretch) { + // compute interpolation scale factor + final float ag = scurve(stretch, 8.0f); + if (DEBUG) { + Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag); + } + + if (stretch < 1.0f) { + // bind vertex buffer + GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); + GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); + + // draw narrow fading white line + setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // clean up + GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); + } + } + + private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { + final float w = dw + (dw * a); + final float h = dh - (dh * a); + final float x = (dw - w) * 0.5f; + final float y = (dh - h) * 0.5f; + setQuad(vtx, x, y, w, h); + } + + private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { + final float w = dw + (dw * a); + final float h = 1.0f; + final float x = (dw - w) * 0.5f; + final float y = (dh - h) * 0.5f; + setQuad(vtx, x, y, w, h); + } + + private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) { + if (DEBUG) { + Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h); + } + vtx.put(0, x); + vtx.put(1, y); + vtx.put(2, x); + vtx.put(3, y + h); + vtx.put(4, x + w); + vtx.put(5, y + h); + vtx.put(6, x + w); + vtx.put(7, y); + } + + private boolean captureScreenshotTextureAndSetViewport() { + // TODO: Use a SurfaceTexture to avoid the extra texture upload. + Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight, + 0, ELECTRON_BEAM_LAYER - 1); + if (bitmap == null) { + Slog.e(TAG, "Could not take a screenshot!"); + return false; + } + try { + if (!attachEglContext()) { + return false; + } + try { + if (!mTexNamesGenerated) { + GLES10.glGenTextures(1, mTexNames, 0); + if (checkGlErrors("glGenTextures")) { + return false; + } + mTexNamesGenerated = true; + } + + GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]); + if (checkGlErrors("glBindTexture")) { + return false; + } + + float u = 1.0f; + float v = 1.0f; + GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0); + if (checkGlErrors("glTexImage2D, first try", false)) { + // Try a power of two size texture instead. + int tw = nextPowerOfTwo(mDisplayWidth); + int th = nextPowerOfTwo(mDisplayHeight); + int format = GLUtils.getInternalFormat(bitmap); + GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0, + format, tw, th, 0, + format, GLES10.GL_UNSIGNED_BYTE, null); + if (checkGlErrors("glTexImage2D, second try")) { + return false; + } + + GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap); + if (checkGlErrors("glTexSubImage2D")) { + return false; + } + + u = (float)mDisplayWidth / tw; + v = (float)mDisplayHeight / th; + } + + // Set up texture coordinates for a quad. + // We might need to change this if the texture ends up being + // a different size from the display for some reason. + mTexCoordBuffer.put(0, 0f); + mTexCoordBuffer.put(1, v); + mTexCoordBuffer.put(2, 0f); + mTexCoordBuffer.put(3, 0f); + mTexCoordBuffer.put(4, u); + mTexCoordBuffer.put(5, 0f); + mTexCoordBuffer.put(6, u); + mTexCoordBuffer.put(7, v); + + // Set up our viewport. + GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight); + GLES10.glMatrixMode(GLES10.GL_PROJECTION); + GLES10.glLoadIdentity(); + GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1); + GLES10.glMatrixMode(GLES10.GL_MODELVIEW); + GLES10.glLoadIdentity(); + GLES10.glMatrixMode(GLES10.GL_TEXTURE); + GLES10.glLoadIdentity(); + } finally { + detachEglContext(); + } + } finally { + bitmap.recycle(); + } + return true; + } + + private void destroyScreenshotTexture() { + if (mTexNamesGenerated) { + mTexNamesGenerated = false; + if (attachEglContext()) { + try { + GLES10.glDeleteTextures(1, mTexNames, 0); + checkGlErrors("glDeleteTextures"); + } finally { + detachEglContext(); + } + } + } + } + + private boolean createEglContext() { + if (mEglDisplay == null) { + mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); + if (mEglDisplay == EGL14.EGL_NO_DISPLAY) { + logEglError("eglGetDisplay"); + return false; + } + + int[] version = new int[2]; + if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) { + mEglDisplay = null; + logEglError("eglInitialize"); + return false; + } + } + + if (mEglConfig == null) { + int[] eglConfigAttribList = new int[] { + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + EGL14.EGL_NONE + }; + int[] numEglConfigs = new int[1]; + EGLConfig[] eglConfigs = new EGLConfig[1]; + if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0, + eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) { + logEglError("eglChooseConfig"); + return false; + } + mEglConfig = eglConfigs[0]; + } + + if (mEglContext == null) { + int[] eglContextAttribList = new int[] { + EGL14.EGL_NONE + }; + mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, + EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0); + if (mEglContext == null) { + logEglError("eglCreateContext"); + return false; + } + } + return true; + } + + /* not used because it is too expensive to create / destroy contexts all of the time + private void destroyEglContext() { + if (mEglContext != null) { + if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) { + logEglError("eglDestroyContext"); + } + mEglContext = null; + } + }*/ + + private boolean createEglSurface() { + if (mSurfaceSession == null) { + mSurfaceSession = new SurfaceSession(); + } + + Surface.openTransaction(); + try { + if (mSurface == null) { + try { + mSurface = new Surface(mSurfaceSession, Process.myPid(), + "ElectronBeam", mDisplayLayerStack, mDisplayWidth, mDisplayHeight, + PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN); + } catch (Surface.OutOfResourcesException ex) { + Slog.e(TAG, "Unable to create surface.", ex); + return false; + } + } + + mSurface.setSize(mDisplayWidth, mDisplayHeight); + + switch (mDisplayRotation) { + case Surface.ROTATION_0: + mSurface.setPosition(0, 0); + mSurface.setMatrix(1, 0, 0, 1); + break; + case Surface.ROTATION_90: + mSurface.setPosition(0, mDisplayWidth); + mSurface.setMatrix(0, -1, 1, 0); + break; + case Surface.ROTATION_180: + mSurface.setPosition(mDisplayWidth, mDisplayHeight); + mSurface.setMatrix(-1, 0, 0, -1); + break; + case Surface.ROTATION_270: + mSurface.setPosition(mDisplayHeight, 0); + mSurface.setMatrix(0, 1, -1, 0); + break; + } + } finally { + Surface.closeTransaction(); + } + + if (mEglSurface == null) { + int[] eglSurfaceAttribList = new int[] { + EGL14.EGL_NONE + }; + mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, + eglSurfaceAttribList, 0); + if (mEglSurface == null) { + logEglError("eglCreateWindowSurface"); + return false; + } + } + return true; + } + + private void destroyEglSurface() { + if (mEglSurface != null) { + if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) { + logEglError("eglDestroySurface"); + } + mEglSurface = null; + } + + if (mSurface != null) { + Surface.openTransaction(); + try { + mSurface.destroy(); + } finally { + Surface.closeTransaction(); + } + mSurface = null; + mSurfaceVisible = false; + } + } + + private boolean showEglSurface() { + if (!mSurfaceVisible) { + Surface.openTransaction(); + try { + mSurface.setLayer(ELECTRON_BEAM_LAYER); + mSurface.show(); + } finally { + Surface.closeTransaction(); + } + mSurfaceVisible = true; + } + return true; + } + + private boolean attachEglContext() { + if (mEglSurface == null) { + return false; + } + if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + logEglError("eglMakeCurrent"); + return false; + } + return true; + } + + private void detachEglContext() { + if (mEglDisplay != null) { + EGL14.eglMakeCurrent(mEglDisplay, + EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); + } + } + + /** + * Interpolates a value in the range 0 .. 1 along a sigmoid curve + * yielding a result in the range 0 .. 1 scaled such that: + * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1. + */ + private static float scurve(float value, float s) { + // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s). + // Here we take the input datum and shift it by 0.5 so that the + // domain spans the range -0.5 .. 0.5 instead of 0 .. 1. + final float x = value - 0.5f; + + // Next apply the sigmoid function to the scaled value + // which produces a value in the range 0 .. 1 so we subtract + // 0.5 to get a value in the range -0.5 .. 0.5 instead. + final float y = sigmoid(x, s) - 0.5f; + + // To obtain the desired boundary conditions we need to scale + // the result so that it fills a range of -1 .. 1. + final float v = sigmoid(0.5f, s) - 0.5f; + + // And finally remap the value back to a range of 0 .. 1. + return y / v * 0.5f + 0.5f; + } + + private static float sigmoid(float x, float s) { + return 1.0f / (1.0f + FloatMath.exp(-x * s)); + } + + private static int nextPowerOfTwo(int value) { + return 1 << (32 - Integer.numberOfLeadingZeros(value)); + } + + private static FloatBuffer createNativeFloatBuffer(int size) { + ByteBuffer bb = ByteBuffer.allocateDirect(size * 4); + bb.order(ByteOrder.nativeOrder()); + return bb.asFloatBuffer(); + } + + private static void logEglError(String func) { + Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable()); + } + + private static boolean checkGlErrors(String func) { + return checkGlErrors(func, true); + } + + private static boolean checkGlErrors(String func, boolean log) { + boolean hadError = false; + int error; + while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) { + if (log) { + Slog.e(TAG, func + " failed: error " + error, new Throwable()); + } + hadError = true; + } + return hadError; + } + + public void dump(PrintWriter pw) { + pw.println(); + pw.println("Electron Beam State:"); + pw.println(" mPrepared=" + mPrepared); + pw.println(" mWarmUp=" + mWarmUp); + pw.println(" mDisplayLayerStack=" + mDisplayLayerStack); + pw.println(" mDisplayRotation=" + mDisplayRotation); + pw.println(" mDisplayWidth=" + mDisplayWidth); + pw.println(" mDisplayHeight=" + mDisplayHeight); + pw.println(" mSurfaceVisible=" + mSurfaceVisible); + } +} diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java new file mode 100644 index 0000000..75f8445 --- /dev/null +++ b/services/java/com/android/server/power/Notifier.java @@ -0,0 +1,441 @@ +/* + * 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; + +import com.android.internal.app.IBatteryStats; +import com.android.server.EventLogTags; + +import android.app.ActivityManagerNative; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +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. + * + * 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. + * + * 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. + */ +final class Notifier { + private static final String TAG = "PowerManagerNotifier"; + + private static final boolean DEBUG = false; + + private static final int POWER_STATE_UNKNOWN = 0; + private static final int POWER_STATE_AWAKE = 1; + private static final int POWER_STATE_ASLEEP = 2; + + private static final int MSG_USER_ACTIVITY = 1; + private static final int MSG_BROADCAST = 2; + + private final Object mLock = new Object(); + + private final Context mContext; + private final IBatteryStats mBatteryStats; + private final SuspendBlocker mSuspendBlocker; + private final WindowManagerPolicy mPolicy; + private final ScreenOnListener mScreenOnListener; + + private final NotifierHandler mHandler; + private final Intent mScreenOnIntent; + private final Intent mScreenOffIntent; + + // The current power state. + private int mActualPowerState; + private int mLastGoToSleepReason; + + // The currently broadcasted power state. This reflects what other parts of the + // system have observed. + private int mBroadcastedPowerState; + private boolean mBroadcastInProgress; + private long mBroadcastStartTime; + + // True if a user activity message should be sent. + private boolean mUserActivityPending; + + public Notifier(Looper looper, Context context, IBatteryStats batteryStats, + SuspendBlocker suspendBlocker, WindowManagerPolicy policy, + ScreenOnListener screenOnListener) { + mContext = context; + mBatteryStats = batteryStats; + mSuspendBlocker = suspendBlocker; + mPolicy = policy; + mScreenOnListener = screenOnListener; + + mHandler = new NotifierHandler(looper); + mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); + mScreenOnIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF); + mScreenOffIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + } + + /** + * Called when a wake lock is acquired. + */ + public void onWakeLockAcquired(int flags, String tag, int ownerUid, int ownerPid, + WorkSource workSource) { + if (DEBUG) { + Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag + + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + + ", workSource=" + workSource); + } + + try { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (workSource != null) { + mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, monitorType); + } else { + mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, monitorType); + } + } catch (RemoteException ex) { + // Ignore + } + } + + /** + * Called when a wake lock is released. + */ + public void onWakeLockReleased(int flags, String tag, int ownerUid, int ownerPid, + WorkSource workSource) { + if (DEBUG) { + Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag + + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + + ", workSource=" + workSource); + } + + try { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (workSource != null) { + mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType); + } else { + mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType); + } + } catch (RemoteException ex) { + // Ignore + } + } + + private static int getBatteryStatsWakeLockMonitorType(int flags) { + switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.PARTIAL_WAKE_LOCK: + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + return BatteryStats.WAKE_TYPE_PARTIAL; + default: + return BatteryStats.WAKE_TYPE_FULL; + } + } + + /** + * Called when the screen is turned on. + */ + public void onScreenOn() { + if (DEBUG) { + Slog.d(TAG, "onScreenOn"); + } + + try { + mBatteryStats.noteScreenOn(); + } catch (RemoteException ex) { + // Ignore + } + } + + /** + * Called when the screen is turned off. + */ + public void onScreenOff() { + if (DEBUG) { + Slog.d(TAG, "onScreenOff"); + } + + try { + mBatteryStats.noteScreenOff(); + } catch (RemoteException ex) { + // Ignore + } + } + + /** + * Called when the screen changes brightness. + */ + public void onScreenBrightness(int brightness) { + if (DEBUG) { + Slog.d(TAG, "onScreenBrightness: brightness=" + brightness); + } + + try { + mBatteryStats.noteScreenBrightness(brightness); + } catch (RemoteException ex) { + // Ignore + } + } + + /** + * Called when the device is waking up from sleep and the + * display is about to be turned on. + */ + public void onWakeUpStarted() { + if (DEBUG) { + Slog.d(TAG, "onWakeUpStarted"); + } + + synchronized (mLock) { + if (mActualPowerState != POWER_STATE_AWAKE) { + mActualPowerState = POWER_STATE_AWAKE; + updatePendingBroadcastLocked(); + } + } + } + + /** + * Called when the device has finished waking up from sleep + * and the display has been turned on. + */ + public void onWakeUpFinished() { + if (DEBUG) { + Slog.d(TAG, "onWakeUpFinished"); + } + } + + /** + * Called when the device is going to sleep. + */ + public void onGoToSleepStarted(int reason) { + if (DEBUG) { + Slog.d(TAG, "onGoToSleepStarted"); + } + + synchronized (mLock) { + mLastGoToSleepReason = reason; + } + } + + /** + * Called when the device has finished going to sleep and the + * display has been turned off. + * + * This is a good time to make transitions that we don't want the user to see, + * such as bringing the key guard to focus. There's no guarantee for this, + * however because the user could turn the device on again at any time. + * Some things may need to be protected by other mechanisms that defer screen on. + */ + public void onGoToSleepFinished() { + if (DEBUG) { + Slog.d(TAG, "onGoToSleepFinished"); + } + + synchronized (mLock) { + if (mActualPowerState != POWER_STATE_ASLEEP) { + mActualPowerState = POWER_STATE_ASLEEP; + if (mUserActivityPending) { + mUserActivityPending = false; + mHandler.removeMessages(MSG_USER_ACTIVITY); + } + updatePendingBroadcastLocked(); + } + } + } + + /** + * Called when there has been user activity. + */ + public void onUserActivity(int event, int uid) { + if (DEBUG) { + Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid); + } + + try { + mBatteryStats.noteUserActivity(uid, event); + } catch (RemoteException ex) { + // Ignore + } + + synchronized (mLock) { + if (!mUserActivityPending) { + mUserActivityPending = true; + Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } + } + + private void updatePendingBroadcastLocked() { + if (!mBroadcastInProgress + && mActualPowerState != POWER_STATE_UNKNOWN + && mActualPowerState != mBroadcastedPowerState) { + mBroadcastInProgress = true; + mSuspendBlocker.acquire(); + Message msg = mHandler.obtainMessage(MSG_BROADCAST); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } + + private void sendUserActivity() { + synchronized (mLock) { + if (!mUserActivityPending) { + return; + } + mUserActivityPending = false; + } + + mPolicy.userActivity(); + } + + private void sendNextBroadcast() { + final int powerState; + final int goToSleepReason; + synchronized (mLock) { + if (mActualPowerState == POWER_STATE_UNKNOWN + || mActualPowerState == mBroadcastedPowerState) { + mBroadcastInProgress = false; + mSuspendBlocker.release(); + return; + } + + powerState = mActualPowerState; + goToSleepReason = mLastGoToSleepReason; + + mBroadcastedPowerState = powerState; + mBroadcastStartTime = SystemClock.uptimeMillis(); + } + + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1); + + if (powerState == POWER_STATE_AWAKE) { + sendWakeUpBroadcast(); + } else { + sendGoToSleepBroadcast(goToSleepReason); + } + } + + private void sendWakeUpBroadcast() { + if (DEBUG) { + Slog.d(TAG, "Sending wake up broadcast."); + } + + EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0); + + mPolicy.screenTurningOn(mScreenOnListener); + try { + ActivityManagerNative.getDefault().wakingUp(); + } catch (RemoteException e) { + // ignore it + } + + if (ActivityManagerNative.isSystemReady()) { + mContext.sendOrderedBroadcast(mScreenOnIntent, null, + mWakeUpBroadcastDone, mHandler, 0, null, null); + } else { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1); + sendNextBroadcast(); + } + } + + private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1, + SystemClock.uptimeMillis() - mBroadcastStartTime, 1); + sendNextBroadcast(); + } + }; + + private void sendGoToSleepBroadcast(int reason) { + if (DEBUG) { + Slog.d(TAG, "Sending go to sleep broadcast."); + } + + int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER; + switch (reason) { + case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: + why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN; + break; + case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: + why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT; + break; + } + + EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0); + + mPolicy.screenTurnedOff(why); + try { + ActivityManagerNative.getDefault().goingToSleep(); + } catch (RemoteException e) { + // ignore it. + } + + if (ActivityManagerNative.isSystemReady()) { + mContext.sendOrderedBroadcast(mScreenOffIntent, null, + mGoToSleepBroadcastDone, mHandler, 0, null, null); + } else { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1); + sendNextBroadcast(); + } + } + + private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0, + SystemClock.uptimeMillis() - mBroadcastStartTime, 1); + sendNextBroadcast(); + } + }; + + private final class NotifierHandler extends Handler { + public NotifierHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_USER_ACTIVITY: + sendUserActivity(); + break; + + case MSG_BROADCAST: + sendNextBroadcast(); + break; + } + } + } +} diff --git a/services/java/com/android/server/power/PhotonicModulator.java b/services/java/com/android/server/power/PhotonicModulator.java new file mode 100644 index 0000000..f7c9c7d --- /dev/null +++ b/services/java/com/android/server/power/PhotonicModulator.java @@ -0,0 +1,87 @@ +/* + * 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; + +import com.android.server.LightsService; + +import java.util.concurrent.Executor; + +/** + * Sets the value of a light asynchronously. + * + * This is done to avoid blocking the looper on devices for which + * setting the backlight brightness is especially slow. + */ +final class PhotonicModulator { + private static final int UNKNOWN_LIGHT_VALUE = -1; + + private final Object mLock = new Object(); + + private final LightsService.Light mLight; + private final Executor mExecutor; + private final SuspendBlocker mSuspendBlocker; + + private boolean mPendingChange; + private int mPendingLightValue; + private int mActualLightValue; + + public PhotonicModulator(Executor executor, LightsService.Light light, + SuspendBlocker suspendBlocker) { + mExecutor = executor; + mLight = light; + mSuspendBlocker = suspendBlocker; + mPendingLightValue = UNKNOWN_LIGHT_VALUE; + mActualLightValue = UNKNOWN_LIGHT_VALUE; + } + + /** + * Asynchronously sets the backlight brightness. + * + * @param lightValue The new light value, from 0 to 255. + */ + public void setBrightness(int lightValue) { + synchronized (mLock) { + if (lightValue != mPendingLightValue) { + mPendingLightValue = lightValue; + if (!mPendingChange) { + mPendingChange = true; + mSuspendBlocker.acquire(); + mExecutor.execute(mTask); + } + } + } + } + + private final Runnable mTask = new Runnable() { + @Override + public void run() { + for (;;) { + final int newLightValue; + synchronized (mLock) { + newLightValue = mPendingLightValue; + if (newLightValue == mActualLightValue) { + mSuspendBlocker.release(); + mPendingChange = false; + return; + } + mActualLightValue = newLightValue; + } + mLight.setBrightness(newLightValue); + } + } + }; +} diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java new file mode 100644 index 0000000..6d68104 --- /dev/null +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -0,0 +1,2121 @@ +/* + * Copyright (C) 2007 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; + +import com.android.internal.app.IBatteryStats; +import com.android.server.BatteryService; +import com.android.server.EventLogTags; +import com.android.server.LightsService; +import com.android.server.TwilightService; +import com.android.server.Watchdog; +import com.android.server.am.ActivityManagerService; +import com.android.server.display.DisplayManagerService; + +import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.BatteryManager; +import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.IPowerManager; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.WorkSource; +import android.provider.Settings; +import android.service.dreams.IDreamManager; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.util.TimeUtils; +import android.view.WindowManagerPolicy; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +import libcore.util.Objects; + +/** + * The power manager service is responsible for coordinating power management + * functions on the device. + */ +public final class PowerManagerService extends IPowerManager.Stub + implements Watchdog.Monitor { + private static final String TAG = "PowerManagerService"; + + private static final boolean DEBUG = false; + private static final boolean DEBUG_SPEW = DEBUG && true; + + // Message: Sent when a user activity timeout occurs to update the power state. + 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; + + // Dirty bit: mWakeLocks changed + private static final int DIRTY_WAKE_LOCKS = 1 << 0; + // Dirty bit: mWakefulness changed + private static final int DIRTY_WAKEFULNESS = 1 << 1; + // Dirty bit: user activity was poked or may have timed out + private static final int DIRTY_USER_ACTIVITY = 1 << 2; + // Dirty bit: actual display power state was updated asynchronously + private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 << 3; + // Dirty bit: mBootCompleted changed + private static final int DIRTY_BOOT_COMPLETED = 1 << 4; + // Dirty bit: settings changed + private static final int DIRTY_SETTINGS = 1 << 5; + // Dirty bit: mIsPowered changed + private static final int DIRTY_IS_POWERED = 1 << 6; + // Dirty bit: mStayOn changed + private static final int DIRTY_STAY_ON = 1 << 7; + // Dirty bit: battery state changed + private static final int DIRTY_BATTERY_STATE = 1 << 8; + + // 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. + private static final int WAKEFULNESS_ASLEEP = 0; + // Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep(). + // When the user activity timeout expires, the device may start napping. + private static final int WAKEFULNESS_AWAKE = 1; + // Wakefulness: The device is napping. It is deciding whether to dream or go to sleep + // but hasn't gotten around to it yet. It can be awoken by a call to wakeUp(), which + // ends the nap. User activity may brighten the screen but does not end the nap. + private static final int WAKEFULNESS_NAPPING = 2; + // Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(), + // which ends the dream. The device goes to sleep when goToSleep() is called, when + // the dream ends or when unplugged. + // User activity may brighten the screen but does not end the dream. + private static final int WAKEFULNESS_DREAMING = 3; + + // Summarizes the state of all active wakelocks. + private static final int WAKE_LOCK_CPU = 1 << 0; + private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1; + 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; + + // Summarizes the user activity state. + private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0; + private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1; + + // Default and minimum screen off timeout in milliseconds. + private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000; + private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000; + + // The screen dim duration, in seconds. + // This is subtracted from the end of the screen off timeout so the + // minimum screen off timeout should be longer than this. + private static final int SCREEN_DIM_DURATION = 7 * 1000; + + private Context mContext; + private LightsService mLightsService; + private BatteryService mBatteryService; + private IBatteryStats mBatteryStats; + private HandlerThread mHandlerThread; + private PowerManagerHandler mHandler; + private WindowManagerPolicy mPolicy; + private Notifier mNotifier; + private DisplayPowerController mDisplayPowerController; + private SettingsObserver mSettingsObserver; + private IDreamManager mDreamManager; + private LightsService.Light mAttentionLight; + + private final Object mLock = new Object(); + + // A bitfield that indicates what parts of the power state have + // changed and need to be recalculated. + private int mDirty; + + // Indicates whether the device is awake or asleep or somewhere in between. + // This is distinct from the screen power state, which is managed separately. + private int mWakefulness; + + // True if MSG_SANDMAN has been scheduled. + private boolean mSandmanScheduled; + + // Table of all suspend blockers. + // There should only be a few of these. + private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>(); + + // Table of all wake locks acquired by applications. + private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>(); + + // A bitfield that summarizes the state of all active wakelocks. + private int mWakeLockSummary; + + // If true, instructs the display controller to wait for the proximity sensor to + // go negative before turning the screen on. + private boolean mRequestWaitForNegativeProximity; + + // Timestamp of the last time the device was awoken or put to sleep. + private long mLastWakeTime; + private long mLastSleepTime; + + // True if we need to send a wake up or go to sleep finished notification + // when the display is ready. + private boolean mSendWakeUpFinishedNotificationWhenReady; + private boolean mSendGoToSleepFinishedNotificationWhenReady; + + // Timestamp of the last call to user activity. + private long mLastUserActivityTime; + private long mLastUserActivityTimeNoChangeLights; + + // A bitfield that summarizes the effect of the user activity timer. + // A zero value indicates that the user activity timer has expired. + private int mUserActivitySummary; + + // The desired display power state. The actual state may lag behind the + // requested because it is updated asynchronously by the display power controller. + private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest(); + + // The time the screen was last turned off, in elapsedRealtime() timebase. + private long mLastScreenOffEventElapsedRealTime; + + // True if the display power state has been fully applied, which means the display + // is actually on or actually off or whatever was requested. + private boolean mDisplayReady; + + // True if holding a wake-lock to block suspend of the CPU. + private boolean mHoldingWakeLockSuspendBlocker; + + // The suspend blocker used to keep the CPU alive when wake locks have been acquired. + private final SuspendBlocker mWakeLockSuspendBlocker; + + // True if systemReady() has been called. + private boolean mSystemReady; + + // True if boot completed occurred. We keep the screen on until this happens. + private boolean mBootCompleted; + + // True if the device is plugged into a power source. + private boolean mIsPowered; + + // True if the device should wake up when plugged or unplugged. + private boolean mWakeUpWhenPluggedOrUnpluggedConfig; + + // True if dreams are supported on this device. + private boolean mDreamsSupportedConfig; + + // True if dreams are enabled by the user. + private boolean mDreamsEnabledSetting; + + // True if dreams should be activated on sleep. + private boolean mDreamsActivateOnSleepSetting; + + // The screen off timeout setting value in milliseconds. + private int mScreenOffTimeoutSetting; + + // The maximum allowable screen off timeout according to the device + // administration policy. Overrides other settings. + private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE; + + // The stay on while plugged in setting. + // A bitfield of battery conditions under which to make the screen stay on. + private int mStayOnWhilePluggedInSetting; + + // True if the device should stay on. + private boolean mStayOn; + + // Screen brightness setting limits. + private int mScreenBrightnessSettingMinimum; + private int mScreenBrightnessSettingMaximum; + private int mScreenBrightnessSettingDefault; + + // The screen brightness setting, from 0 to 255. + // Use -1 if no value has been set. + private int mScreenBrightnessSetting; + + // The screen auto-brightness adjustment setting, from -1 to 1. + // Use 0 if there is no adjustment. + private float mScreenAutoBrightnessAdjustmentSetting; + + // The screen brightness mode. + // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants. + private int mScreenBrightnessModeSetting; + + // The screen brightness setting override from the window manager + // to allow the current foreground activity to override the brightness. + // Use -1 to disable. + private int mScreenBrightnessOverrideFromWindowManager = -1; + + // The screen brightness setting override from the settings application + // to temporarily adjust the brightness until next updated, + // Use -1 to disable. + private int mTemporaryScreenBrightnessSettingOverride = -1; + + // The screen brightness adjustment setting override from the settings + // application to temporarily adjust the auto-brightness adjustment factor + // until next updated, in the range -1..1. + // Use NaN to disable. + private float mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN; + + private native void nativeInit(); + private static native void nativeShutdown(); + private static native void nativeReboot(String reason) throws IOException; + + 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); + + public PowerManagerService() { + synchronized (mLock) { + mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService"); + mWakeLockSuspendBlocker.acquire(); + mHoldingWakeLockSuspendBlocker = true; + mWakefulness = WAKEFULNESS_AWAKE; + } + + nativeInit(); + nativeSetPowerState(true, true); + } + + /** + * Initialize the power manager. + * Must be called before any other functions within the power manager are called. + */ + 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; + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + mHandler = new PowerManagerHandler(mHandlerThread.getLooper()); + + Watchdog.getInstance().addMonitor(this); + } + + public void setPolicy(WindowManagerPolicy policy) { + synchronized (mLock) { + mPolicy = policy; + } + } + + public void systemReady(TwilightService twilight) { + synchronized (mLock) { + mSystemReady = true; + + PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting(); + mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); + mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting(); + + mNotifier = new Notifier(mHandler.getLooper(), mContext, mBatteryStats, + createSuspendBlockerLocked("PowerManagerService.Broadcasts"), + mPolicy, mScreenOnListener); + mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(), + mContext, mNotifier, mLightsService, twilight, + createSuspendBlockerLocked("PowerManagerService.Display"), + mDisplayPowerControllerCallbacks, mHandler); + + mSettingsObserver = new SettingsObserver(mHandler); + mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION); + + // Register for broadcasts from other components of the system. + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + mContext.registerReceiver(new BatteryReceiver(), filter); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BOOT_COMPLETED); + mContext.registerReceiver(new BootCompletedReceiver(), filter); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DOCK_EVENT); + mContext.registerReceiver(new DockReceiver(), filter); + + // Register for settings changes. + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ENABLED), false, mSettingsObserver); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_OFF_TIMEOUT), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STAY_ON_WHILE_PLUGGED_IN), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS), false, mSettingsObserver); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS_MODE), false, mSettingsObserver); + + // Go. + readConfigurationLocked(); + updateSettingsLocked(); + mDirty |= DIRTY_BATTERY_STATE; + updatePowerStateLocked(); + } + } + + private void readConfigurationLocked() { + final Resources resources = mContext.getResources(); + + mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean( + com.android.internal.R.bool.config_unplugTurnsOnScreen); + mDreamsSupportedConfig = resources.getBoolean( + com.android.internal.R.bool.config_enableDreams); + } + + private void updateSettingsLocked() { + final ContentResolver resolver = mContext.getContentResolver(); + + mDreamsEnabledSetting = (Settings.Secure.getInt(resolver, + Settings.Secure.SCREENSAVER_ENABLED, 0) != 0); + mDreamsActivateOnSleepSetting = (Settings.Secure.getInt(resolver, + Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 0) != 0); + mScreenOffTimeoutSetting = Settings.System.getInt(resolver, + Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT); + mStayOnWhilePluggedInSetting = Settings.System.getInt(resolver, + Settings.System.STAY_ON_WHILE_PLUGGED_IN, + BatteryManager.BATTERY_PLUGGED_AC); + + final int oldScreenBrightnessSetting = mScreenBrightnessSetting; + mScreenBrightnessSetting = Settings.System.getInt(resolver, + Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault); + if (oldScreenBrightnessSetting != mScreenBrightnessSetting) { + mTemporaryScreenBrightnessSettingOverride = -1; + } + + final float oldScreenAutoBrightnessAdjustmentSetting = + mScreenAutoBrightnessAdjustmentSetting; + mScreenAutoBrightnessAdjustmentSetting = Settings.System.getFloat(resolver, + Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f); + if (oldScreenAutoBrightnessAdjustmentSetting != mScreenAutoBrightnessAdjustmentSetting) { + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN; + } + + mScreenBrightnessModeSetting = Settings.System.getInt(resolver, + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); + + mDirty |= DIRTY_SETTINGS; + } + + private void handleSettingsChangedLocked() { + updateSettingsLocked(); + updatePowerStateLocked(); + } + + @Override // Binder call + public void acquireWakeLock(IBinder lock, int flags, String tag, WorkSource ws) { + if (lock == null) { + throw new IllegalArgumentException("lock must not be null"); + } + PowerManager.validateWakeLockParameters(flags, tag); + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + if (ws != null && ws.size() != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, null); + } else { + ws = null; + } + + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); + final long ident = Binder.clearCallingIdentity(); + try { + acquireWakeLockInternal(lock, flags, tag, ws, uid, pid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws, + int uid, int pid) { + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock) + + ", flags=0x" + Integer.toHexString(flags) + + ", tag=\"" + tag + "\", ws=" + ws + ", uid=" + uid + ", pid=" + pid); + } + + WakeLock wakeLock; + int index = findWakeLockIndexLocked(lock); + if (index >= 0) { + wakeLock = mWakeLocks.get(index); + if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) { + // Update existing wake lock. This shouldn't happen but is harmless. + notifyWakeLockReleasedLocked(wakeLock); + wakeLock.updateProperties(flags, tag, ws, uid, pid); + notifyWakeLockAcquiredLocked(wakeLock); + } + } else { + wakeLock = new WakeLock(lock, flags, tag, ws, uid, pid); + try { + lock.linkToDeath(wakeLock, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("Wake lock is already dead."); + } + notifyWakeLockAcquiredLocked(wakeLock); + mWakeLocks.add(wakeLock); + } + + applyWakeLockFlagsOnAcquireLocked(wakeLock); + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); + } + } + + private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock) { + if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { + wakeUpNoUpdateLocked(SystemClock.uptimeMillis()); + } + } + + @Override // Binder call + public void releaseWakeLock(IBinder lock, int flags) { + if (lock == null) { + throw new IllegalArgumentException("lock must not be null"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + + final long ident = Binder.clearCallingIdentity(); + try { + releaseWakeLockInternal(lock, flags); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void releaseWakeLockInternal(IBinder lock, int flags) { + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock) + + ", flags=0x" + Integer.toHexString(flags)); + } + + int index = findWakeLockIndexLocked(lock); + if (index < 0) { + return; + } + + WakeLock wakeLock = mWakeLocks.get(index); + mWakeLocks.remove(index); + notifyWakeLockReleasedLocked(wakeLock); + wakeLock.mLock.unlinkToDeath(wakeLock, 0); + + if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) { + mRequestWaitForNegativeProximity = true; + } + + applyWakeLockFlagsOnReleaseLocked(wakeLock); + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); + } + } + + private void handleWakeLockDeath(WakeLock wakeLock) { + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "handleWakeLockDeath: lock=" + Objects.hashCode(wakeLock.mLock)); + } + + int index = mWakeLocks.indexOf(wakeLock); + if (index < 0) { + return; + } + + mWakeLocks.remove(index); + notifyWakeLockReleasedLocked(wakeLock); + + applyWakeLockFlagsOnReleaseLocked(wakeLock); + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); + } + } + + private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) { + if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0) { + userActivityNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_OTHER, + PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS, + wakeLock.mOwnerUid); + } + } + + @Override // Binder call + public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) { + if (lock == null) { + throw new IllegalArgumentException("lock must not be null"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + if (ws != null && ws.size() != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, null); + } else { + ws = null; + } + + final long ident = Binder.clearCallingIdentity(); + try { + updateWakeLockWorkSourceInternal(lock, ws); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws) { + synchronized (mLock) { + int index = findWakeLockIndexLocked(lock); + if (index < 0) { + throw new IllegalArgumentException("Wake lock not active"); + } + + WakeLock wakeLock = mWakeLocks.get(index); + if (!wakeLock.hasSameWorkSource(ws)) { + notifyWakeLockReleasedLocked(wakeLock); + wakeLock.updateWorkSource(ws); + notifyWakeLockAcquiredLocked(wakeLock); + } + } + } + + private int findWakeLockIndexLocked(IBinder lock) { + final int count = mWakeLocks.size(); + for (int i = 0; i < count; i++) { + if (mWakeLocks.get(i).mLock == lock) { + return i; + } + } + return -1; + } + + private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) { + if (mSystemReady) { + mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, + wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource); + } + } + + private void notifyWakeLockReleasedLocked(WakeLock wakeLock) { + if (mSystemReady) { + mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag, + wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource); + } + } + + @Override // Binder call + public boolean isWakeLockLevelSupported(int level) { + final long ident = Binder.clearCallingIdentity(); + try { + return isWakeLockLevelSupportedInternal(level); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private boolean isWakeLockLevelSupportedInternal(int level) { + synchronized (mLock) { + switch (level) { + case PowerManager.PARTIAL_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.FULL_WAKE_LOCK: + return true; + + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + return mSystemReady && mDisplayPowerController.isProximitySensorAvailable(); + + default: + return false; + } + } + } + + @Override // Binder call + public void userActivity(long eventTime, int event, int flags) { + if (eventTime > SystemClock.uptimeMillis()) { + throw new IllegalArgumentException("event time must not be in the future"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + userActivityInternal(eventTime, event, flags, uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // Called from native code. + private void userActivityFromNative(long eventTime, int event, int flags) { + userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID); + } + + private void userActivityInternal(long eventTime, int event, int flags, int uid) { + synchronized (mLock) { + if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) { + updatePowerStateLocked(); + } + } + } + + private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) { + if (DEBUG_SPEW) { + Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime + + ", event=" + event + ", flags=0x" + Integer.toHexString(flags) + + ", uid=" + uid); + } + + if (eventTime < mLastSleepTime || eventTime < mLastWakeTime + || mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) { + return false; + } + + mNotifier.onUserActivity(event, uid); + + if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) { + if (eventTime > mLastUserActivityTimeNoChangeLights + && eventTime > mLastUserActivityTime) { + mLastUserActivityTimeNoChangeLights = eventTime; + mDirty |= DIRTY_USER_ACTIVITY; + return true; + } + } else { + if (eventTime > mLastUserActivityTime) { + mLastUserActivityTime = eventTime; + mDirty |= DIRTY_USER_ACTIVITY; + return true; + } + } + return false; + } + + @Override // Binder call + public void wakeUp(long eventTime) { + if (eventTime > SystemClock.uptimeMillis()) { + throw new IllegalArgumentException("event time must not be in the future"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + wakeUpInternal(eventTime); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // Called from native code. + private void wakeUpFromNative(long eventTime) { + wakeUpInternal(eventTime); + } + + private void wakeUpInternal(long eventTime) { + synchronized (mLock) { + if (wakeUpNoUpdateLocked(eventTime)) { + updatePowerStateLocked(); + } + } + } + + private boolean wakeUpNoUpdateLocked(long eventTime) { + if (DEBUG_SPEW) { + Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime); + } + + if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE + || !mBootCompleted || !mSystemReady) { + return false; + } + + switch (mWakefulness) { + case WAKEFULNESS_ASLEEP: + Slog.i(TAG, "Waking up from sleep..."); + mNotifier.onWakeUpStarted(); + mSendWakeUpFinishedNotificationWhenReady = true; + mSendGoToSleepFinishedNotificationWhenReady = false; + break; + case WAKEFULNESS_DREAMING: + Slog.i(TAG, "Waking up from dream..."); + break; + case WAKEFULNESS_NAPPING: + Slog.i(TAG, "Waking up from nap..."); + break; + } + + mLastWakeTime = eventTime; + mWakefulness = WAKEFULNESS_AWAKE; + mDirty |= DIRTY_WAKEFULNESS; + + userActivityNoUpdateLocked( + eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + return true; + } + + @Override // Binder call + public void goToSleep(long eventTime, int reason) { + if (eventTime > SystemClock.uptimeMillis()) { + throw new IllegalArgumentException("event time must not be in the future"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + goToSleepInternal(eventTime, reason); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // Called from native code. + private void goToSleepFromNative(long eventTime, int reason) { + goToSleepInternal(eventTime, reason); + } + + private void goToSleepInternal(long eventTime, int reason) { + synchronized (mLock) { + if (goToSleepNoUpdateLocked(eventTime, reason)) { + updatePowerStateLocked(); + } + } + } + + private boolean goToSleepNoUpdateLocked(long eventTime, int reason) { + if (DEBUG_SPEW) { + Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason); + } + + if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP + || !mBootCompleted || !mSystemReady) { + return false; + } + + switch (reason) { + case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: + Slog.i(TAG, "Going to sleep due to device administration policy..."); + break; + case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: + Slog.i(TAG, "Going to sleep due to screen timeout..."); + break; + default: + Slog.i(TAG, "Going to sleep by user request..."); + reason = PowerManager.GO_TO_SLEEP_REASON_USER; + break; + } + + 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; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + numWakeLocksCleared += 1; + break; + } + } + EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared); + return true; + } + + /** + * Updates the global power state based on dirty bits recorded in mDirty. + * + * This is the main function that performs power state transitions. + * We centralize them here so that we can recompute the power state completely + * each time something important changes, and ensure that we do it the same + * way each time. The point is to gather all of the transition logic here. + */ + private void updatePowerStateLocked() { + if (!mSystemReady || mDirty == 0) { + return; + } + + // Phase 0: Basic state updates. + updateIsPoweredLocked(mDirty); + updateStayOnLocked(mDirty); + + // Phase 1: Update wakefulness. + // Loop because the wake lock and user activity computations are influenced + // by changes in wakefulness. + final long now = SystemClock.uptimeMillis(); + int dirtyPhase2 = 0; + for (;;) { + int dirtyPhase1 = mDirty; + dirtyPhase2 |= dirtyPhase1; + mDirty = 0; + + updateWakeLockSummaryLocked(dirtyPhase1); + updateUserActivitySummaryLocked(now, dirtyPhase1); + if (!updateWakefulnessLocked(dirtyPhase1)) { + break; + } + } + + // Phase 2: Update dreams and display power state. + updateDreamLocked(dirtyPhase2); + updateDisplayPowerStateLocked(dirtyPhase2); + + // Phase 3: Send notifications, if needed. + sendPendingNotificationsLocked(); + + // Phase 4: Update suspend blocker. + // Because we might release the last suspend blocker here, we need to make sure + // we finished everything else first! + updateSuspendBlockerLocked(); + } + + private void sendPendingNotificationsLocked() { + if (mDisplayReady) { + if (mSendWakeUpFinishedNotificationWhenReady) { + mSendWakeUpFinishedNotificationWhenReady = false; + mNotifier.onWakeUpFinished(); + } + if (mSendGoToSleepFinishedNotificationWhenReady) { + mSendGoToSleepFinishedNotificationWhenReady = false; + mNotifier.onGoToSleepFinished(); + } + } + } + + /** + * Updates the value of mIsPowered. + * Sets DIRTY_IS_POWERED if a change occurred. + */ + private void updateIsPoweredLocked(int dirty) { + if ((dirty & DIRTY_BATTERY_STATE) != 0) { + boolean wasPowered = mIsPowered; + mIsPowered = mBatteryService.isPowered(); + + if (wasPowered != mIsPowered) { + mDirty |= DIRTY_IS_POWERED; + + // Treat plugging and unplugging the devices as a user activity. + // Users find it disconcerting when they plug or unplug the device + // and it shuts off right away. + // Some devices also wake the device when plugged or unplugged because + // they don't have a charging LED. + final long now = SystemClock.uptimeMillis(); + if (mWakeUpWhenPluggedOrUnpluggedConfig) { + wakeUpNoUpdateLocked(now); + } + userActivityNoUpdateLocked( + now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + } + } + } + + /** + * Updates the value of mStayOn. + * Sets DIRTY_STAY_ON if a change occurred. + */ + private void updateStayOnLocked(int dirty) { + if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) { + if (mStayOnWhilePluggedInSetting != 0 + && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { + mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting); + } else { + mStayOn = false; + } + } + } + + /** + * Updates the value of mWakeLockSummary to summarize the state of all active wake locks. + * Note that most wake-locks are ignored when the system is asleep. + * + * This function must have no other side-effects. + */ + private void updateWakeLockSummaryLocked(int dirty) { + if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) { + mWakeLockSummary = 0; + + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.PARTIAL_WAKE_LOCK: + mWakeLockSummary |= WAKE_LOCK_CPU; + break; + case PowerManager.FULL_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU + | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; + } + break; + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT; + } + break; + case PowerManager.SCREEN_DIM_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM; + } + break; + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + if (mWakefulness != WAKEFULNESS_ASLEEP) { + mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_PROXIMITY_SCREEN_OFF; + } + break; + } + } + + if (DEBUG_SPEW) { + Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness=" + + wakefulnessToString(mWakefulness) + + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)); + } + } + } + + /** + * Updates the value of mUserActivitySummary to summarize the user requested + * state of the system such as whether the screen should be bright or dim. + * Note that user activity is ignored when the system is asleep. + * + * This function must have no other side-effects. + */ + private void updateUserActivitySummaryLocked(long now, int dirty) { + // Update the status of the user activity timeout timer. + if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) { + mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT); + + long nextTimeout = 0; + if (mWakefulness != WAKEFULNESS_ASLEEP) { + final int screenOffTimeout = getScreenOffTimeoutLocked(); + final int screenDimDuration = getScreenDimDurationLocked(); + + mUserActivitySummary = 0; + if (mLastUserActivityTime >= mLastWakeTime) { + nextTimeout = mLastUserActivityTime + + screenOffTimeout - screenDimDuration; + if (now < nextTimeout) { + mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT; + } else { + nextTimeout = mLastUserActivityTime + screenOffTimeout; + if (now < nextTimeout) { + mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM; + } + } + } + if (mUserActivitySummary == 0 + && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) { + nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; + if (now < nextTimeout + && mDisplayPowerRequest.screenState + != DisplayPowerRequest.SCREEN_STATE_OFF) { + mUserActivitySummary = mDisplayPowerRequest.screenState + == DisplayPowerRequest.SCREEN_STATE_BRIGHT ? + USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM; + } + } + if (mUserActivitySummary != 0) { + Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, nextTimeout); + } + } else { + mUserActivitySummary = 0; + } + + if (DEBUG_SPEW) { + Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness=" + + wakefulnessToString(mWakefulness) + + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) + + ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout)); + } + } + } + + /** + * Called when a user activity timeout has occurred. + * Simply indicates that something about user activity has changed so that the new + * state can be recomputed when the power state is updated. + * + * This function must have no other side-effects besides setting the dirty + * bit and calling update power state. Wakefulness transitions are handled elsewhere. + */ + private void handleUserActivityTimeout() { // runs on handler thread + synchronized (mLock) { + if (DEBUG_SPEW) { + Slog.d(TAG, "handleUserActivityTimeout"); + } + + mDirty |= DIRTY_USER_ACTIVITY; + updatePowerStateLocked(); + } + } + + private int getScreenOffTimeoutLocked() { + int timeout = mScreenOffTimeoutSetting; + if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) { + timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin); + } + return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT); + } + + private int getScreenDimDurationLocked() { + return SCREEN_DIM_DURATION; + } + + /** + * Updates the wakefulness of the device. + * + * This is the function that decides whether the device should start napping + * based on the current wake locks and user activity state. It may modify mDirty + * if the wakefulness changes. + * + * Returns true if the wakefulness changed and we need to restart power state calculation. + */ + 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) { + if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { + if (DEBUG_SPEW) { + Slog.d(TAG, "updateWakefulnessLocked: Nap time..."); + } + mWakefulness = WAKEFULNESS_NAPPING; + mDirty |= DIRTY_WAKEFULNESS; + changed = true; + } + } + return changed; + } + + // Also used when exiting a dream to determine whether we should go back + // to being fully awake or else go to sleep for good. + private boolean isItBedTimeYetLocked() { + return mBootCompleted && !mStayOn + && (mWakeLockSummary + & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) == 0 + && (mUserActivitySummary + & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0; + } + + /** + * Determines whether to post a message to the sandman to update the dream state. + */ + private void updateDreamLocked(int dirty) { + if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_SETTINGS + | DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_BATTERY_STATE)) != 0) { + scheduleSandmanLocked(); + } + } + + private void scheduleSandmanLocked() { + if (!mSandmanScheduled) { + mSandmanScheduled = true; + Message msg = mHandler.obtainMessage(MSG_SANDMAN); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } + + /** + * Called when the device enters or exits a napping or dreaming state. + * + * We do this asynchronously because we must call out of the power manager to start + * the dream and we don't want to hold our lock while doing so. There is a risk that + * the device will wake or go to sleep in the meantime so we have to handle that case. + */ + private void handleSandman() { // runs on handler thread + // Handle preconditions. + boolean startDreaming = false; + synchronized (mLock) { + mSandmanScheduled = false; + boolean canDream = canDreamLocked(); + if (DEBUG_SPEW) { + Log.d(TAG, "handleSandman: canDream=" + canDream + + ", mWakefulness=" + wakefulnessToString(mWakefulness)); + } + + if (canDream && mWakefulness == WAKEFULNESS_NAPPING) { + startDreaming = true; + } + } + + // Get the dream manager, if needed. + if (startDreaming && mDreamManager == null) { + mDreamManager = IDreamManager.Stub.asInterface( + ServiceManager.checkService("dreams")); + if (mDreamManager == null) { + Slog.w(TAG, "Unable to find IDreamManager."); + } + } + + // Start dreaming if needed. + // We only control the dream on the handler thread, so we don't need to worry about + // concurrent attempts to start or stop the dream. + boolean isDreaming = false; + if (mDreamManager != null) { + try { + isDreaming = mDreamManager.isDreaming(); + if (startDreaming && !isDreaming) { + Slog.i(TAG, "Entering dreamland."); + mDreamManager.dream(); + isDreaming = mDreamManager.isDreaming(); + if (!isDreaming) { + Slog.i(TAG, "Could not enter dreamland. Sleep will be dreamless."); + } + } + } catch (RemoteException ex) { + } + } + + // Update dream state. + // We might need to stop the dream again if the preconditions changed. + boolean continueDreaming = false; + synchronized (mLock) { + if (isDreaming && canDreamLocked()) { + if (mWakefulness == WAKEFULNESS_NAPPING) { + mWakefulness = WAKEFULNESS_DREAMING; + mDirty |= DIRTY_WAKEFULNESS; + updatePowerStateLocked(); + continueDreaming = true; + } else if (mWakefulness == WAKEFULNESS_DREAMING) { + continueDreaming = true; + } + } + if (!continueDreaming) { + handleDreamFinishedLocked(); + } + + // Allow the sandman to detect when the dream has ended. + // FIXME: The DreamManagerService should tell us explicitly. + if (mWakefulness == WAKEFULNESS_DREAMING + || mWakefulness == WAKEFULNESS_NAPPING) { + if (!mSandmanScheduled) { + mSandmanScheduled = true; + Message msg = mHandler.obtainMessage(MSG_SANDMAN); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, 1000); + } + } + } + + // Stop dreaming if needed. + // It's possible that something else changed to make us need to start the dream again. + // If so, then the power manager will have posted another message to the handler + // to take care of it later. + if (mDreamManager != null) { + try { + if (!continueDreaming && isDreaming) { + Slog.i(TAG, "Leaving dreamland."); + mDreamManager.awaken(); + } + } catch (RemoteException ex) { + } + } + } + + /** + * Returns true if the device is allowed to dream in its current state, + * assuming there has been no recent user activity and no wake locks are held. + */ + private boolean canDreamLocked() { + return mIsPowered + && mDreamsSupportedConfig + && mDreamsEnabledSetting + && mDreamsActivateOnSleepSetting + && !mBatteryService.isBatteryLow(); + } + + /** + * Called when a dream is ending to figure out what to do next. + */ + private void handleDreamFinishedLocked() { + if (mWakefulness == WAKEFULNESS_NAPPING + || mWakefulness == WAKEFULNESS_DREAMING) { + if (isItBedTimeYetLocked()) { + goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); + updatePowerStateLocked(); + } else { + wakeUpNoUpdateLocked(SystemClock.uptimeMillis()); + updatePowerStateLocked(); + } + } + } + + + /** + * Updates the display power state asynchronously. + * When the update is finished, mDisplayReady will be set to true. The display + * controller posts a message to tell us when the actual display power state + * has been updated so we come back here to double-check and finish up. + * + * This function recalculates the display power state each time. + */ + 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(); + if (newScreenState != mDisplayPowerRequest.screenState) { + if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF + && mDisplayPowerRequest.screenState + != DisplayPowerRequest.SCREEN_STATE_OFF) { + mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime(); + } + + mDisplayPowerRequest.screenState = newScreenState; + nativeSetPowerState( + newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF, + newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT); + } + + int screenBrightness = mScreenBrightnessSettingDefault; + float screenAutoBrightnessAdjustment = 0.0f; + boolean autoBrightness = (mScreenBrightnessModeSetting == + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { + screenBrightness = mScreenBrightnessOverrideFromWindowManager; + autoBrightness = false; + } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) { + screenBrightness = mTemporaryScreenBrightnessSettingOverride; + } else if (isValidBrightness(mScreenBrightnessSetting)) { + screenBrightness = mScreenBrightnessSetting; + } + if (autoBrightness) { + screenBrightness = mScreenBrightnessSettingDefault; + if (isValidAutoBrightnessAdjustment( + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) { + screenAutoBrightnessAdjustment = + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride; + } else if (isValidAutoBrightnessAdjustment( + mScreenAutoBrightnessAdjustmentSetting)) { + screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting; + } + } + screenBrightness = Math.max(Math.min(screenBrightness, + mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum); + screenAutoBrightnessAdjustment = Math.max(Math.min( + screenAutoBrightnessAdjustment, 1.0f), -1.0f); + mDisplayPowerRequest.screenBrightness = screenBrightness; + mDisplayPowerRequest.screenAutoBrightnessAdjustment = + screenAutoBrightnessAdjustment; + mDisplayPowerRequest.useAutoBrightness = autoBrightness; + + mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); + + mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest, + mRequestWaitForNegativeProximity); + mRequestWaitForNegativeProximity = false; + + if (DEBUG_SPEW) { + Slog.d(TAG, "updateScreenStateLocked: displayReady=" + mDisplayReady + + ", newScreenState=" + newScreenState + + ", mWakefulness=" + mWakefulness + + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) + + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) + + ", mBootCompleted=" + mBootCompleted); + } + } + } + + private static boolean isValidBrightness(int value) { + return value >= 0 && value <= 255; + } + + private static boolean isValidAutoBrightnessAdjustment(float value) { + // Handles NaN by always returning false. + return value >= -1.0f && value <= 1.0f; + } + + private int getDesiredScreenPowerState() { + if (mWakefulness == WAKEFULNESS_ASLEEP) { + return DisplayPowerRequest.SCREEN_STATE_OFF; + } + + if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 + || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0 + || !mBootCompleted) { + return DisplayPowerRequest.SCREEN_STATE_BRIGHT; + } + + return DisplayPowerRequest.SCREEN_STATE_DIM; + } + + private final DisplayPowerController.Callbacks mDisplayPowerControllerCallbacks = + new DisplayPowerController.Callbacks() { + @Override + public void onStateChanged() { + mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED; + updatePowerStateLocked(); + } + + @Override + public void onProximityNegative() { + userActivityNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + updatePowerStateLocked(); + } + }; + + private boolean shouldUseProximitySensorLocked() { + return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0; + } + + /** + * Updates the suspend blocker that keeps the CPU alive. + * + * This function must have no other side-effects. + */ + private void updateSuspendBlockerLocked() { + boolean wantCpu = isCpuNeededLocked(); + if (wantCpu != mHoldingWakeLockSuspendBlocker) { + mHoldingWakeLockSuspendBlocker = wantCpu; + if (wantCpu) { + if (DEBUG) { + Slog.d(TAG, "updateSuspendBlockerLocked: Acquiring suspend blocker."); + } + mWakeLockSuspendBlocker.acquire(); + } else { + if (DEBUG) { + Slog.d(TAG, "updateSuspendBlockerLocked: Releasing suspend blocker."); + } + mWakeLockSuspendBlocker.release(); + } + } + } + + private boolean isCpuNeededLocked() { + return !mBootCompleted + || mWakeLockSummary != 0 + || mUserActivitySummary != 0 + || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF + || !mDisplayReady; + } + + @Override // Binder call + public boolean isScreenOn() { + final long ident = Binder.clearCallingIdentity(); + try { + return isScreenOnInternal(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private boolean isScreenOnInternal() { + synchronized (mLock) { + return !mSystemReady + || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF; + } + } + + private void handleBatteryStateChangedLocked() { + mDirty |= DIRTY_BATTERY_STATE; + updatePowerStateLocked(); + } + + private void handleBootCompletedLocked() { + final long now = SystemClock.uptimeMillis(); + mBootCompleted = true; + mDirty |= DIRTY_BOOT_COMPLETED; + userActivityNoUpdateLocked( + now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + updatePowerStateLocked(); + } + + private void handleDockStateChangedLocked(int dockState) { + // TODO + } + + /** + * Reboot the device immediately, passing 'reason' (may be null) + * to the underlying __reboot system call. Should not return. + */ + @Override // Binder call + public void reboot(String reason) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); + + final long ident = Binder.clearCallingIdentity(); + try { + rebootInternal(reason); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void rebootInternal(final String reason) { + if (mHandler == null || !mSystemReady) { + throw new IllegalStateException("Too early to call reboot()"); + } + + Runnable runnable = new Runnable() { + public void run() { + synchronized (this) { + ShutdownThread.reboot(mContext, reason, false); + } + } + }; + + // ShutdownThread must run on a looper capable of displaying the UI. + Message msg = Message.obtain(mHandler, runnable); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + + // PowerManager.reboot() is documented not to return so just wait for the inevitable. + synchronized (runnable) { + while (true) { + try { + runnable.wait(); + } catch (InterruptedException e) { + } + } + } + } + + /** + * Crash the runtime (causing a complete restart of the Android framework). + * Requires REBOOT permission. Mostly for testing. Should not return. + */ + @Override // Binder call + public void crash(String message) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); + + final long ident = Binder.clearCallingIdentity(); + try { + crashInternal(message); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void crashInternal(final String message) { + Thread t = new Thread("PowerManagerService.crash()") { + public void run() { + throw new RuntimeException(message); + } + }; + try { + t.start(); + t.join(); + } catch (InterruptedException e) { + Log.wtf(TAG, e); + } + } + + @Override // Binder call + public void clearUserActivityTimeout(long now, long timeout) { + // TODO Auto-generated method stub + // Only used by phone app, delete this + } + + @Override // Binder call + public void setPokeLock(int pokey, IBinder lock, String tag) { + // TODO Auto-generated method stub + // Only used by phone app, delete this + } + + /** + * Set the setting that determines whether the device stays on when plugged in. + * The argument is a bit string, with each bit specifying a power source that, + * when the device is connected to that source, causes the device to stay on. + * See {@link android.os.BatteryManager} for the list of power sources that + * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC} + * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB} + * + * Used by "adb shell svc power stayon ..." + * + * @param val an {@code int} containing the bits that specify which power sources + * should cause the device to stay on. + */ + @Override // Binder call + public void setStayOnSetting(int val) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setStayOnSettingInternal(val); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void setStayOnSettingInternal(int val) { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.STAY_ON_WHILE_PLUGGED_IN, val); + } + + /** + * Used by device administration to set the maximum screen off timeout. + * + * This method must only be called by the device administration policy manager. + */ + @Override // Binder call + public void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs) { + final long ident = Binder.clearCallingIdentity(); + try { + setMaximumScreenOffTimeoutFromDeviceAdminInternal(timeMs); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) { + synchronized (mLock) { + mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs; + mDirty |= DIRTY_SETTINGS; + updatePowerStateLocked(); + } + } + + private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() { + return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0 + && mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE; + } + + @Override // Binder call + public void preventScreenOn(boolean prevent) { + // TODO Auto-generated method stub + // Only used by phone app, delete this + } + + /** + * Used by the phone application to make the attention LED flash when ringing. + */ + @Override // Binder call + public void setAttentionLight(boolean on, int color) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setAttentionLightInternal(on, color); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void setAttentionLightInternal(boolean on, int color) { + LightsService.Light light; + synchronized (mLock) { + if (!mSystemReady) { + return; + } + light = mAttentionLight; + } + + // Control light outside of lock. + light.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + } + + /** + * Used by the Watchdog. + */ + public long timeSinceScreenWasLastOn() { + synchronized (mLock) { + if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) { + return 0; + } + return SystemClock.elapsedRealtime() - mLastScreenOffEventElapsedRealTime; + } + } + + /** + * Used by the window manager to override the screen brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or -1 to disable the override. + */ + public void setScreenBrightnessOverrideFromWindowManager(int brightness) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setScreenBrightnessOverrideFromWindowManagerInternal(brightness); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) { + synchronized (mLock) { + if (mScreenBrightnessOverrideFromWindowManager != brightness) { + mScreenBrightnessOverrideFromWindowManager = brightness; + mDirty |= DIRTY_SETTINGS; + updatePowerStateLocked(); + } + } + } + + /** + * Used by the window manager to override the button brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or -1 to disable the override. + */ + public void setButtonBrightnessOverrideFromWindowManager(int brightness) { + // Do nothing. + // Button lights are not currently supported in the new implementation. + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + } + + /** + * Used by the settings application and brightness control widgets to + * temporarily override the current screen brightness setting so that the + * user can observe the effect of an intended settings change without applying + * it immediately. + * + * The override will be canceled when the setting value is next updated. + * + * @param brightness The overridden brightness. + * + * @see Settings.System#SCREEN_BRIGHTNESS + */ + @Override // Binder call + public void setTemporaryScreenBrightnessSettingOverride(int brightness) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setTemporaryScreenBrightnessSettingOverrideInternal(brightness); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) { + synchronized (mLock) { + if (mTemporaryScreenBrightnessSettingOverride != brightness) { + mTemporaryScreenBrightnessSettingOverride = brightness; + mDirty |= DIRTY_SETTINGS; + updatePowerStateLocked(); + } + } + } + + /** + * Used by the settings application and brightness control widgets to + * temporarily override the current screen auto-brightness adjustment setting so that the + * user can observe the effect of an intended settings change without applying + * it immediately. + * + * The override will be canceled when the setting value is next updated. + * + * @param adj The overridden brightness, or Float.NaN to disable the override. + * + * @see Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ + */ + @Override // Binder call + public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) { + synchronized (mLock) { + // Note: This condition handles NaN because NaN is not equal to any other + // value, including itself. + if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) { + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj; + mDirty |= DIRTY_SETTINGS; + updatePowerStateLocked(); + } + } + } + + /** + * Low-level function turn the device off immediately, without trying + * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. + */ + public static void lowLevelShutdown() { + nativeShutdown(); + } + + /** + * Low-level function to reboot the device. + * + * @param reason code to pass to the kernel (e.g. "recovery"), or null. + * @throws IOException if reboot fails for some reason (eg, lack of + * permission) + */ + public static void lowLevelReboot(String reason) throws IOException { + nativeReboot(reason); + } + + @Override // Watchdog.Monitor implementation + public void monitor() { + // Grab and release lock for watchdog monitor to detect deadlocks. + synchronized (mLock) { + } + } + + @Override // Binder call + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump PowerManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + pw.println("POWER MANAGER (dumpsys power)\n"); + + final DisplayPowerController dpc; + synchronized (mLock) { + pw.println("Power Manager State:"); + pw.println(" mDirty=0x" + Integer.toHexString(mDirty)); + pw.println(" mWakefulness=" + wakefulnessToString(mWakefulness)); + pw.println(" mIsPowered=" + mIsPowered); + pw.println(" mStayOn=" + mStayOn); + pw.println(" mBootCompleted=" + mBootCompleted); + pw.println(" mSystemReady=" + mSystemReady); + pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)); + pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)); + pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); + pw.println(" mSandmanScheduled=" + mSandmanScheduled); + pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); + pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); + pw.println(" mSendWakeUpFinishedNotificationWhenReady=" + + mSendWakeUpFinishedNotificationWhenReady); + pw.println(" mSendGoToSleepFinishedNotificationWhenReady=" + + mSendGoToSleepFinishedNotificationWhenReady); + pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime)); + pw.println(" mLastUserActivityTimeNoChangeLights=" + + TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights)); + pw.println(" mDisplayReady=" + mDisplayReady); + pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker); + + pw.println(); + pw.println("Settings and Configuration:"); + pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig); + pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); + pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); + pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting); + pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin=" + + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced=" + + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")"); + pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting); + pw.println(" mScreenBrightnessSetting=" + mScreenBrightnessSetting); + pw.println(" mScreenAutoBrightnessAdjustmentSetting=" + + mScreenAutoBrightnessAdjustmentSetting); + pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting); + pw.println(" mScreenBrightnessOverrideFromWindowManager=" + + mScreenBrightnessOverrideFromWindowManager); + pw.println(" mTemporaryScreenBrightnessSettingOverride=" + + mTemporaryScreenBrightnessSettingOverride); + pw.println(" mTemporaryScreenAutoBrightnessAdjustmentSettingOverride=" + + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride); + pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum); + pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum); + pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault); + + pw.println(); + pw.println("Wake Locks: size=" + mWakeLocks.size()); + for (WakeLock wl : mWakeLocks) { + pw.println(" " + wl); + } + + pw.println(); + pw.println("Suspend Blockers: size=" + mSuspendBlockers.size()); + for (SuspendBlocker sb : mSuspendBlockers) { + pw.println(" " + sb); + } + + dpc = mDisplayPowerController; + } + + if (dpc != null) { + dpc.dump(pw); + } + } + + private SuspendBlocker createSuspendBlockerLocked(String name) { + SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name); + mSuspendBlockers.add(suspendBlocker); + return suspendBlocker; + } + + private static String wakefulnessToString(int wakefulness) { + switch (wakefulness) { + case WAKEFULNESS_ASLEEP: + return "Asleep"; + case WAKEFULNESS_AWAKE: + return "Awake"; + case WAKEFULNESS_DREAMING: + return "Dreaming"; + case WAKEFULNESS_NAPPING: + return "Napping"; + default: + return Integer.toString(wakefulness); + } + } + + private static WorkSource copyWorkSource(WorkSource workSource) { + return workSource != null ? new WorkSource(workSource) : null; + } + + private final class BatteryReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + handleBatteryStateChangedLocked(); + } + } + } + + private final class BootCompletedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + handleBootCompletedLocked(); + } + } + } + + private final class DockReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + handleDockStateChangedLocked(dockState); + } + } + } + + private final class SettingsObserver extends ContentObserver { + public SettingsObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + synchronized (mLock) { + handleSettingsChangedLocked(); + } + } + } + + private final WindowManagerPolicy.ScreenOnListener mScreenOnListener = + new WindowManagerPolicy.ScreenOnListener() { + @Override + public void onScreenOn() { + } + }; + + /** + * Handler for asynchronous operations performed by the power manager. + */ + private final class PowerManagerHandler extends Handler { + public PowerManagerHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_USER_ACTIVITY_TIMEOUT: + handleUserActivityTimeout(); + break; + case MSG_SANDMAN: + handleSandman(); + break; + } + } + } + + /** + * Represents a wake lock that has been acquired by an application. + */ + private final class WakeLock implements IBinder.DeathRecipient { + public final IBinder mLock; + public int mFlags; + public String mTag; + public WorkSource mWorkSource; + public int mOwnerUid; + public int mOwnerPid; + + public WakeLock(IBinder lock, int flags, String tag, WorkSource workSource, + int ownerUid, int ownerPid) { + mLock = lock; + mFlags = flags; + mTag = tag; + mWorkSource = copyWorkSource(workSource); + mOwnerUid = ownerUid; + mOwnerPid = ownerPid; + } + + @Override + public void binderDied() { + PowerManagerService.this.handleWakeLockDeath(this); + } + + public boolean hasSameProperties(int flags, String tag, WorkSource workSource, + int ownerUid, int ownerPid) { + return mFlags == flags + && mTag.equals(tag) + && hasSameWorkSource(workSource) + && mOwnerUid == ownerUid + && mOwnerPid == ownerPid; + } + + public void updateProperties(int flags, String tag, WorkSource workSource, + int ownerUid, int ownerPid) { + mFlags = flags; + mTag = tag; + updateWorkSource(workSource); + mOwnerUid = ownerUid; + mOwnerPid = ownerPid; + } + + public boolean hasSameWorkSource(WorkSource workSource) { + return Objects.equal(mWorkSource, workSource); + } + + public void updateWorkSource(WorkSource workSource) { + mWorkSource = copyWorkSource(workSource); + } + + @Override + public String toString() { + return getLockLevelString() + + " '" + mTag + "'" + getLockFlagsString() + + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")"; + } + + private String getLockLevelString() { + switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + return "FULL_WAKE_LOCK "; + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + return "SCREEN_BRIGHT_WAKE_LOCK "; + case PowerManager.SCREEN_DIM_WAKE_LOCK: + return "SCREEN_DIM_WAKE_LOCK "; + case PowerManager.PARTIAL_WAKE_LOCK: + return "PARTIAL_WAKE_LOCK "; + case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: + return "PROXIMITY_SCREEN_OFF_WAKE_LOCK"; + default: + return "??? "; + } + } + + private String getLockFlagsString() { + String result = ""; + if ((mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { + result += " ACQUIRE_CAUSES_WAKEUP"; + } + if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) { + result += " ON_AFTER_RELEASE"; + } + return result; + } + } + + private final class SuspendBlockerImpl implements SuspendBlocker { + private final String mName; + private int mReferenceCount; + + public SuspendBlockerImpl(String name) { + mName = name; + } + + @Override + protected void finalize() throws Throwable { + try { + if (mReferenceCount != 0) { + Log.wtf(TAG, "Suspend blocker \"" + mName + + "\" was finalized without being released!"); + mReferenceCount = 0; + nativeReleaseSuspendBlocker(mName); + } + } finally { + super.finalize(); + } + } + + @Override + public void acquire() { + synchronized (this) { + mReferenceCount += 1; + if (mReferenceCount == 1) { + nativeAcquireSuspendBlocker(mName); + } + } + } + + @Override + public void release() { + synchronized (this) { + mReferenceCount -= 1; + if (mReferenceCount == 0) { + nativeReleaseSuspendBlocker(mName); + } else if (mReferenceCount < 0) { + Log.wtf(TAG, "Suspend blocker \"" + mName + + "\" was released without being acquired!", new Throwable()); + mReferenceCount = 0; + } + } + } + + @Override + public String toString() { + synchronized (this) { + return mName + ": ref count=" + mReferenceCount; + } + } + } +} diff --git a/services/java/com/android/server/power/RampAnimator.java b/services/java/com/android/server/power/RampAnimator.java new file mode 100644 index 0000000..6f063c3 --- /dev/null +++ b/services/java/com/android/server/power/RampAnimator.java @@ -0,0 +1,131 @@ +/* + * 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; + +import android.animation.ValueAnimator; +import android.util.IntProperty; +import android.view.Choreographer; + +/** + * A custom animator that progressively updates a property value at + * a given variable rate until it reaches a particular target value. + */ +final class RampAnimator<T> { + private final T mObject; + private final IntProperty<T> mProperty; + private final Choreographer mChoreographer; + + private int mCurrentValue; + private int mTargetValue; + private int mRate; + + private boolean mAnimating; + private float mAnimatedValue; // higher precision copy of mCurrentValue + private long mLastFrameTimeNanos; + + private boolean mFirstTime = true; + + public RampAnimator(T object, IntProperty<T> property) { + mObject = object; + mProperty = property; + mChoreographer = Choreographer.getInstance(); + } + + /** + * Starts animating towards the specified value. + * + * If this is the first time the property is being set, the value jumps + * directly to the target. + * + * @param target The target value. + * @param rate The convergence rate, in units per second. + * @return True if the target differs from the previous target. + */ + public boolean animateTo(int target, int rate) { + // Immediately jump to the target the first time. + if (mFirstTime) { + mFirstTime = false; + mProperty.setValue(mObject, target); + mCurrentValue = target; + return true; + } + + // Adjust the rate based on the closest target. + // If a faster rate is specified, then use the new rate so that we converge + // more rapidly based on the new request. + // If a slower rate is specified, then use the new rate only if the current + // value is somewhere in between the new and the old target meaning that + // we will be ramping in a different direction to get there. + // Otherwise, continue at the previous rate. + if (!mAnimating + || rate > mRate + || (target <= mCurrentValue && mCurrentValue <= mTargetValue) + || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) { + mRate = rate; + } + + final boolean changed = (mTargetValue != target); + mTargetValue = target; + + // Start animating. + if (!mAnimating && target != mCurrentValue) { + mAnimating = true; + mAnimatedValue = mCurrentValue; + mLastFrameTimeNanos = System.nanoTime(); + postCallback(); + } + + return changed; + } + + private void postCallback() { + mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mCallback, null); + } + + private final Runnable mCallback = new Runnable() { + @Override // Choreographer callback + public void run() { + final long frameTimeNanos = mChoreographer.getFrameTimeNanos(); + final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos) + * 0.000000001f; + final float amount = timeDelta * mRate / ValueAnimator.getDurationScale(); + mLastFrameTimeNanos = frameTimeNanos; + + // Advance the animated value towards the target at the specified rate + // and clamp to the target. This gives us the new current value but + // we keep the animated value around to allow for fractional increments + // towards the target. + int oldCurrentValue = mCurrentValue; + if (mTargetValue > mCurrentValue) { + mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue); + } else { + mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue); + } + mCurrentValue = (int)Math.round(mAnimatedValue); + + if (oldCurrentValue != mCurrentValue) { + mProperty.setValue(mObject, mCurrentValue); + } + + if (mTargetValue != mCurrentValue) { + postCallback(); + } else { + mAnimating = false; + } + } + }; +} diff --git a/services/java/com/android/server/pm/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java index 3675d41..a3770d7 100644 --- a/services/java/com/android/server/pm/ShutdownThread.java +++ b/services/java/com/android/server/power/ShutdownThread.java @@ -15,7 +15,7 @@ */ -package com.android.server.pm; +package com.android.server.power; import android.app.ActivityManagerNative; import android.app.AlertDialog; @@ -23,7 +23,7 @@ import android.app.Dialog; import android.app.IActivityManager; import android.app.ProgressDialog; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothManager; import android.nfc.NfcAdapter; import android.nfc.INfcAdapter; import android.content.BroadcastReceiver; @@ -43,7 +43,6 @@ import android.os.storage.IMountService; import android.os.storage.IMountShutdownObserver; import com.android.internal.telephony.ITelephony; -import com.android.server.PowerManagerService; import android.util.Log; import android.view.WindowManager; @@ -385,9 +384,9 @@ public final class ShutdownThread extends Thread { INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc")); final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); - final IBluetooth bluetooth = - IBluetooth.Stub.asInterface(ServiceManager.checkService( - BluetoothAdapter.BLUETOOTH_SERVICE)); + final IBluetoothManager bluetooth = + IBluetoothManager.Stub.asInterface(ServiceManager.checkService( + BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE)); try { nfcOff = nfc == null || @@ -402,8 +401,7 @@ public final class ShutdownThread extends Thread { } try { - bluetoothOff = bluetooth == null || - bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF; + bluetoothOff = bluetooth == null || !bluetooth.isEnabled(); if (!bluetoothOff) { Log.w(TAG, "Disabling Bluetooth..."); bluetooth.disable(false); // disable but don't persist new state @@ -429,8 +427,7 @@ public final class ShutdownThread extends Thread { while (SystemClock.elapsedRealtime() < endTime) { if (!bluetoothOff) { try { - bluetoothOff = - bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF; + bluetoothOff = !bluetooth.isEnabled(); } catch (RemoteException ex) { Log.e(TAG, "RemoteException during bluetooth shutdown", ex); bluetoothOff = true; diff --git a/services/java/com/android/server/power/SuspendBlocker.java b/services/java/com/android/server/power/SuspendBlocker.java new file mode 100644 index 0000000..70b278a --- /dev/null +++ b/services/java/com/android/server/power/SuspendBlocker.java @@ -0,0 +1,43 @@ +/* + * 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 suspend blocker mechanism equivalent to holding a partial wake lock. + * + * This interface is used internally to avoid introducing internal dependencies + * on the high-level wake lock mechanism. + */ +interface SuspendBlocker { + /** + * Acquires the suspend blocker. + * Prevents the CPU from going to sleep. + * + * Calls to acquire() nest and must be matched by the same number + * of calls to release(). + */ + void acquire(); + + /** + * Releases the suspend blocker. + * Allows the CPU to go to sleep if no other suspend blockers are held. + * + * It is an error to call release() if the suspend blocker has not been acquired. + * The system may crash. + */ + void release(); +} diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java new file mode 100644 index 0000000..a3b45c7 --- /dev/null +++ b/services/java/com/android/server/usb/UsbDebuggingManager.java @@ -0,0 +1,322 @@ +/* + * 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 an + * limitations under the License. + */ + +package com.android.server.usb; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.SystemClock; +import android.util.Slog; +import android.util.Base64; + +import java.lang.Thread; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.security.MessageDigest; +import java.util.Arrays; + +public class UsbDebuggingManager implements Runnable { + private static final String TAG = "UsbDebuggingManager"; + private static final boolean DEBUG = false; + + private final String ADBD_SOCKET = "adbd"; + private final String ADB_DIRECTORY = "misc/adb"; + private final String ADB_KEYS_FILE = "adb_keys"; + private final int BUFFER_SIZE = 4096; + + private final Context mContext; + private final Thread mThread; + private final Handler mHandler; + private final HandlerThread mHandlerThread; + private boolean mAdbEnabled = false; + private String mFingerprints; + private LocalSocket mSocket = null; + private OutputStream mOutputStream = null; + + public UsbDebuggingManager(Context context) { + mThread = new Thread(this); + mHandlerThread = new HandlerThread("UsbDebuggingHandler"); + mHandlerThread.start(); + mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper()); + mContext = context; + } + + private void listenToSocket() throws IOException { + try { + byte[] buffer = new byte[BUFFER_SIZE]; + LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET, + LocalSocketAddress.Namespace.RESERVED); + InputStream inputStream = null; + + mSocket = new LocalSocket(); + mSocket.connect(address); + + mOutputStream = mSocket.getOutputStream(); + inputStream = mSocket.getInputStream(); + + while (true) { + int count = inputStream.read(buffer); + if (count < 0) { + Slog.e(TAG, "got " + count + " reading"); + break; + } + + if (buffer[0] == 'P' && buffer[1] == 'K') { + String key = new String(Arrays.copyOfRange(buffer, 2, count)); + Slog.d(TAG, "Received public key: " + key); + Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM); + msg.obj = key; + mHandler.sendMessage(msg); + } + else { + Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2)))); + break; + } + } + } catch (IOException ex) { + Slog.e(TAG, "Communication error: ", ex); + throw ex; + } finally { + closeSocket(); + } + } + + @Override + public void run() { + while (mAdbEnabled) { + try { + listenToSocket(); + } catch (Exception e) { + /* Don't loop too fast if adbd dies, before init restarts it */ + SystemClock.sleep(1000); + } + } + } + + private void closeSocket() { + try { + mOutputStream.close(); + } catch (IOException e) { + Slog.e(TAG, "Failed closing output stream: " + e); + } + + try { + mSocket.close(); + } catch (IOException ex) { + Slog.e(TAG, "Failed closing socket: " + ex); + } + } + + private void sendResponse(String msg) { + if (mOutputStream != null) { + try { + mOutputStream.write(msg.getBytes()); + } + catch (IOException ex) { + Slog.e(TAG, "Failed to write response:", ex); + } + } + } + + class UsbDebuggingHandler extends Handler { + private static final int MESSAGE_ADB_ENABLED = 1; + private static final int MESSAGE_ADB_DISABLED = 2; + private static final int MESSAGE_ADB_ALLOW = 3; + private static final int MESSAGE_ADB_DENY = 4; + private static final int MESSAGE_ADB_CONFIRM = 5; + + public UsbDebuggingHandler(Looper looper) { + super(looper); + } + + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_ADB_ENABLED: + if (mAdbEnabled) + break; + + mAdbEnabled = true; + + mThread.start(); + + break; + + case MESSAGE_ADB_DISABLED: + if (!mAdbEnabled) + break; + + mAdbEnabled = false; + closeSocket(); + + try { + mThread.join(); + } catch (Exception ex) { + } + + mOutputStream = null; + mSocket = null; + + case MESSAGE_ADB_ALLOW: { + String key = (String)msg.obj; + String fingerprints = getFingerprints(key); + + if (!fingerprints.equals(mFingerprints)) { + Slog.e(TAG, "Fingerprints do not match. Got " + + fingerprints + ", expected " + mFingerprints); + break; + } + + if (msg.arg1 == 1) { + writeKey(key); + } + + sendResponse("OK"); + break; + } + + case MESSAGE_ADB_DENY: + sendResponse("NO"); + break; + + case MESSAGE_ADB_CONFIRM: { + String key = (String)msg.obj; + mFingerprints = getFingerprints(key); + showConfirmationDialog(key, mFingerprints); + break; + } + } + } + } + + private String getFingerprints(String key) { + String hex = "0123456789ABCDEF"; + StringBuilder sb = new StringBuilder(); + MessageDigest digester; + + try { + digester = MessageDigest.getInstance("MD5"); + } catch (Exception ex) { + Slog.e(TAG, "Error getting digester: " + ex); + return ""; + } + + byte[] base64_data = key.split("\\s+")[0].getBytes(); + byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT)); + + for (int i = 0; i < digest.length; i++) { + sb.append(hex.charAt((digest[i] >> 4) & 0xf)); + sb.append(hex.charAt(digest[i] & 0xf)); + if (i < digest.length - 1) + sb.append(":"); + } + return sb.toString(); + } + + private void showConfirmationDialog(String key, String fingerprints) { + Intent dialogIntent = new Intent(); + + dialogIntent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbDebuggingActivity"); + dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + dialogIntent.putExtra("key", key); + dialogIntent.putExtra("fingerprints", fingerprints); + try { + mContext.startActivity(dialogIntent); + } catch (ActivityNotFoundException e) { + Slog.e(TAG, "unable to start UsbDebuggingActivity"); + } + } + + private void writeKey(String key) { + File dataDir = Environment.getDataDirectory(); + File adbDir = new File(dataDir, ADB_DIRECTORY); + + if (!adbDir.exists()) { + Slog.e(TAG, "ADB data directory does not exist"); + return; + } + + try { + File keyFile = new File(adbDir, ADB_KEYS_FILE); + + if (!keyFile.exists()) { + keyFile.createNewFile(); + FileUtils.setPermissions(keyFile.toString(), + FileUtils.S_IRUSR | FileUtils.S_IWUSR | + FileUtils.S_IRGRP, -1, -1); + } + + FileOutputStream fo = new FileOutputStream(keyFile, true); + fo.write(key.getBytes()); + fo.write('\n'); + fo.close(); + } + catch (IOException ex) { + Slog.e(TAG, "Error writing key:" + ex); + } + } + + + public void setAdbEnabled(boolean enabled) { + mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED + : UsbDebuggingHandler.MESSAGE_ADB_DISABLED); + } + + public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { + Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW); + msg.arg1 = alwaysAllow ? 1 : 0; + msg.obj = publicKey; + mHandler.sendMessage(msg); + } + + public void denyUsbDebugging() { + mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY); + } + + + public void dump(FileDescriptor fd, PrintWriter pw) { + pw.println(" USB Debugging State:"); + pw.println(" Connected to adbd: " + (mOutputStream != null)); + pw.println(" Last key received: " + mFingerprints); + pw.println(" User keys:"); + try { + pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null)); + } catch (IOException e) { + pw.println("IOException: " + e); + } + pw.println(" System keys:"); + try { + pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null)); + } catch (IOException e) { + pw.println("IOException: " + e); + } + } +} diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index a115345..ddecf14 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -114,6 +114,7 @@ public class UsbDeviceManager { private boolean mAudioSourceEnabled; private Map<String, List<Pair<String, String>>> mOemModeMap; private String[] mAccessoryStrings; + private UsbDebuggingManager mDebuggingManager; private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver() { @@ -166,6 +167,10 @@ public class UsbDeviceManager { if (DEBUG) Slog.d(TAG, "accessory attached at boot"); startAccessoryMode(); } + + if ("1".equals(SystemProperties.get("ro.adb.secure"))) { + mDebuggingManager = new UsbDebuggingManager(context); + } } public void systemReady() { @@ -425,6 +430,9 @@ public class UsbDeviceManager { setEnabledFunctions(mDefaultFunctions, true); updateAdbNotification(); } + if (mDebuggingManager != null) { + mDebuggingManager.setAdbEnabled(mAdbEnabled); + } } private void setEnabledFunctions(String functions, boolean makeDefault) { @@ -601,6 +609,9 @@ public class UsbDeviceManager { if (mCurrentAccessory != null) { mSettingsManager.accessoryAttached(mCurrentAccessory); } + if (mDebuggingManager != null) { + mDebuggingManager.setAdbEnabled(mAdbEnabled); + } break; } } @@ -802,10 +813,25 @@ public class UsbDeviceManager { return usbFunctions; } + public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { + if (mDebuggingManager != null) { + mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey); + } + } + + public void denyUsbDebugging() { + if (mDebuggingManager != null) { + mDebuggingManager.denyUsbDebugging(); + } + } + public void dump(FileDescriptor fd, PrintWriter pw) { if (mHandler != null) { mHandler.dump(fd, pw); } + if (mDebuggingManager != null) { + mDebuggingManager.dump(fd, pw); + } } private native String[] nativeGetAccessoryStrings(); diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java index 0205ef8..bebcd56 100644 --- a/services/java/com/android/server/usb/UsbService.java +++ b/services/java/com/android/server/usb/UsbService.java @@ -164,6 +164,16 @@ public class UsbService extends IUsbManager.Stub { } } + public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey); + } + + public void denyUsbDebugging() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.denyUsbDebugging(); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java index 13e8bc5..c25f010 100644 --- a/services/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/java/com/android/server/wm/AppWindowAnimator.java @@ -10,10 +10,8 @@ import android.view.animation.Animation; import android.view.animation.Transformation; import java.io.PrintWriter; +import java.util.ArrayList; -/** - * - */ public class AppWindowAnimator { static final String TAG = "AppWindowAnimator"; @@ -48,12 +46,15 @@ public class AppWindowAnimator { Animation thumbnailAnimation; final Transformation thumbnailTransformation = new Transformation(); + /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */ + ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<WindowStateAnimator>(); + static final Animation sDummyAnimation = new DummyAnimation(); - public AppWindowAnimator(final WindowManagerService service, final AppWindowToken atoken) { - mService = service; + public AppWindowAnimator(final AppWindowToken atoken) { mAppToken = atoken; - mAnimator = service.mAnimator; + mService = atoken.service; + mAnimator = atoken.mAnimator; } public void setAnimation(Animation anim, boolean initialized) { @@ -123,7 +124,7 @@ public class AppWindowAnimator { if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) { mService.setInputMethodAnimLayerAdjustment(adj); } - if (w == mService.mWallpaperTarget && mService.mLowerWallpaperTarget == null) { + if (w == mAnimator.mWallpaperTarget && mAnimator.mLowerWallpaperTarget == null) { mService.setWallpaperAnimLayerAdjustmentLocked(adj); } } @@ -168,7 +169,7 @@ public class AppWindowAnimator { } transformation.clear(); final boolean more = animation.getTransformation(currentTime, transformation); - if (WindowManagerService.DEBUG_ANIM) Slog.v( + if (false && WindowManagerService.DEBUG_ANIM) Slog.v( TAG, "Stepped animation in " + mAppToken + ": more=" + more + ", xform=" + transformation); if (!more) { animation = null; @@ -255,9 +256,9 @@ public class AppWindowAnimator { transformation.clear(); - final int N = mAppToken.windows.size(); + final int N = mAllAppWinAnimators.size(); for (int i=0; i<N; i++) { - mAppToken.windows.get(i).mWinAnimator.finishExit(); + mAllAppWinAnimators.get(i).finishExit(); } mAppToken.updateReportedVisibilityLocked(); @@ -266,9 +267,9 @@ public class AppWindowAnimator { boolean showAllWindowsLocked() { boolean isAnimating = false; - final int NW = mAppToken.allAppWindows.size(); + final int NW = mAllAppWinAnimators.size(); for (int i=0; i<NW; i++) { - WindowStateAnimator winAnimator = mAppToken.allAppWindows.get(i).mWinAnimator; + WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i); if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator); winAnimator.performShowLocked(); diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java index 6ecbb8e..13b072c 100644 --- a/services/java/com/android/server/wm/AppWindowToken.java +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -104,7 +104,7 @@ class AppWindowToken extends WindowToken { appToken = _token; mInputApplicationHandle = new InputApplicationHandle(this); mAnimator = service.mAnimator; - mAppAnimator = new AppWindowAnimator(_service, this); + mAppAnimator = new AppWindowAnimator(this); } void sendAppVisibilityToClients() { diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java index 27af313..64d2602 100644 --- a/services/java/com/android/server/wm/BlackFrame.java +++ b/services/java/com/android/server/wm/BlackFrame.java @@ -35,7 +35,7 @@ public class BlackFrame { final int layer; final Surface surface; - BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b) + BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack) throws Surface.OutOfResourcesException { left = l; top = t; @@ -44,11 +44,11 @@ public class BlackFrame { int h = b-t; if (WindowManagerService.DEBUG_SURFACE_TRACE) { surface = new WindowStateAnimator.SurfaceTrace(session, 0, "BlackSurface(" - + l + ", " + t + ")", - -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); + + l + ", " + t + ")", layerStack, + w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); } else { - surface = new Surface(session, 0, "BlackSurface", - -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); + surface = new Surface(session, 0, "BlackSurface", layerStack, + w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); } surface.setAlpha(1); surface.setLayer(layer); @@ -103,7 +103,7 @@ public class BlackFrame { } public BlackFrame(SurfaceSession session, Rect outer, Rect inner, - int layer) throws Surface.OutOfResourcesException { + int layer, final int layerStack) throws Surface.OutOfResourcesException { boolean success = false; mOuterRect = new Rect(outer); @@ -111,19 +111,19 @@ public class BlackFrame { try { if (outer.top < inner.top) { mBlackSurfaces[0] = new BlackSurface(session, layer, - outer.left, outer.top, inner.right, inner.top); + outer.left, outer.top, inner.right, inner.top, layerStack); } if (outer.left < inner.left) { mBlackSurfaces[1] = new BlackSurface(session, layer, - outer.left, inner.top, inner.left, outer.bottom); + outer.left, inner.top, inner.left, outer.bottom, layerStack); } if (outer.bottom > inner.bottom) { mBlackSurfaces[2] = new BlackSurface(session, layer, - inner.left, inner.bottom, outer.right, outer.bottom); + inner.left, inner.bottom, outer.right, outer.bottom, layerStack); } if (outer.right > inner.right) { mBlackSurfaces[3] = new BlackSurface(session, layer, - inner.right, outer.top, outer.right, inner.bottom); + inner.right, outer.top, outer.right, inner.bottom, layerStack); } success = true; } finally { diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java index e8f56c8..81daac6 100644 --- a/services/java/com/android/server/wm/DimAnimator.java +++ b/services/java/com/android/server/wm/DimAnimator.java @@ -39,18 +39,18 @@ class DimAnimator { int mLastDimWidth, mLastDimHeight; - DimAnimator (SurfaceSession session) { + DimAnimator (SurfaceSession session, final int layerStack) { if (mDimSurface == null) { try { if (WindowManagerService.DEBUG_SURFACE_TRACE) { mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0, "DimAnimator", - -1, 16, 16, PixelFormat.OPAQUE, + layerStack, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); } else { mDimSurface = new Surface(session, 0, "DimAnimator", - -1, 16, 16, PixelFormat.OPAQUE, + layerStack, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); } if (WindowManagerService.SHOW_TRANSACTIONS || diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java index 9fca418..4ab8ce1 100644 --- a/services/java/com/android/server/wm/DimSurface.java +++ b/services/java/com/android/server/wm/DimSurface.java @@ -30,18 +30,18 @@ class DimSurface { int mLayer = -1; int mLastDimWidth, mLastDimHeight; - DimSurface(SurfaceSession session) { + DimSurface(SurfaceSession session, final int layerStack) { if (mDimSurface == null) { try { if (WindowManagerService.DEBUG_SURFACE_TRACE) { mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0, "DimSurface", - -1, 16, 16, PixelFormat.OPAQUE, + layerStack, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); } else { mDimSurface = new Surface(session, 0, "DimSurface", - -1, 16, 16, PixelFormat.OPAQUE, + layerStack, 16, 16, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); } if (WindowManagerService.SHOW_TRANSACTIONS || diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java new file mode 100644 index 0000000..6e5bbcb --- /dev/null +++ b/services/java/com/android/server/wm/DisplayContent.java @@ -0,0 +1,111 @@ +/* + * 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.wm; + +import android.view.Display; +import android.view.DisplayInfo; + +import java.io.PrintWriter; +import java.util.ArrayList; + +class DisplayContentList extends ArrayList<DisplayContent> { +} + +/** + * Utility class for keeping track of the WindowStates and other pertinent contents of a + * particular Display. + * + * IMPORTANT: No method from this class should ever be used without holding + * WindowManagerService.mWindowMap. + */ +class DisplayContent { + + /** Unique identifier of this stack. */ + private final int mDisplayId; + + /** Z-ordered (bottom-most first) list of all Window objects. Assigned to an element + * from mDisplayWindows; */ + private WindowList mWindows = new WindowList(); + + + // This protects the following display size properties, so that + // getDisplaySize() doesn't need to acquire the global lock. This is + // needed because the window manager sometimes needs to use ActivityThread + // while it has its global state locked (for example to load animation + // resources), but the ActivityThread also needs get the current display + // size sometimes when it has its package lock held. + // + // These will only be modified with both mWindowMap and mDisplaySizeLock + // held (in that order) so the window manager doesn't need to acquire this + // lock when needing these values in its normal operation. + final Object mDisplaySizeLock = new Object(); + int mInitialDisplayWidth = 0; + int mInitialDisplayHeight = 0; + int mInitialDisplayDensity = 0; + int mBaseDisplayWidth = 0; + int mBaseDisplayHeight = 0; + int mBaseDisplayDensity = 0; + final DisplayInfo mDisplayInfo = new DisplayInfo(); + final Display mDisplay; + + DisplayContent(Display display) { + mDisplay = display; + mDisplayId = display.getDisplayId(); + display.getDisplayInfo(mDisplayInfo); + } + + int getDisplayId() { + return mDisplayId; + } + + WindowList getWindowList() { + return mWindows; + } + + Display getDisplay() { + return mDisplay; + } + + DisplayInfo getDisplayInfo() { + return mDisplayInfo; + } + + public void dump(PrintWriter pw) { + pw.print(" Display: mDisplayId="); pw.println(mDisplayId); + pw.print(" init="); pw.print(mInitialDisplayWidth); pw.print("x"); + pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity); + pw.print("dpi"); + if (mInitialDisplayWidth != mBaseDisplayWidth + || mInitialDisplayHeight != mBaseDisplayHeight + || mInitialDisplayDensity != mBaseDisplayDensity) { + pw.print(" base="); + pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight); + pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi"); + } + pw.print(" cur="); + pw.print(mDisplayInfo.logicalWidth); + pw.print("x"); pw.print(mDisplayInfo.logicalHeight); + pw.print(" app="); + pw.print(mDisplayInfo.appWidth); + pw.print("x"); pw.print(mDisplayInfo.appHeight); + pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth); + pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight); + pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth); + pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight); + pw.println(); + } +} diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java index b2cf3e0..3fcf680 100644 --- a/services/java/com/android/server/wm/DragState.java +++ b/services/java/com/android/server/wm/DragState.java @@ -29,6 +29,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.util.Slog; +import android.view.DisplayInfo; import android.view.DragEvent; import android.view.InputChannel; import android.view.Surface; @@ -58,6 +59,7 @@ class DragState { WindowState mTargetWindow; ArrayList<WindowState> mNotifiedWindows; boolean mDragInProgress; + DisplayContent mDisplayContent; private final Region mTmpRegion = new Region(); @@ -84,7 +86,11 @@ class DragState { mNotifiedWindows = null; } - void register() { + /** + * @param displayContent The display parameters associated with the window being dragged. + */ + void register(DisplayContent displayContent) { + mDisplayContent = displayContent; if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel"); if (mClientChannel != null) { Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel"); @@ -101,7 +107,8 @@ class DragState { mDragApplicationHandle.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null); + mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, + mDisplayContent.getDisplayId()); mDragWindowHandle.name = "drag"; mDragWindowHandle.inputChannel = mServerChannel; mDragWindowHandle.layer = getDragLayerLw(); @@ -125,8 +132,9 @@ class DragState { // The drag window covers the entire display mDragWindowHandle.frameLeft = 0; mDragWindowHandle.frameTop = 0; - mDragWindowHandle.frameRight = mService.mCurDisplayWidth; - mDragWindowHandle.frameBottom = mService.mCurDisplayHeight; + DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); + mDragWindowHandle.frameRight = displayInfo.logicalWidth; + mDragWindowHandle.frameBottom = displayInfo.logicalHeight; // Pause rotations before a drag. if (WindowManagerService.DEBUG_ORIENTATION) { @@ -179,9 +187,10 @@ class DragState { Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); } - final int N = mService.mWindows.size(); + final WindowList windows = mDisplayContent.getWindowList(); + final int N = windows.size(); for (int i = 0; i < N; i++) { - sendDragStartedLw(mService.mWindows.get(i), touchX, touchY, mDataDescription); + sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription); } } @@ -380,7 +389,8 @@ class DragState { WindowState touchedWin = null; final int x = (int) xf; final int y = (int) yf; - final ArrayList<WindowState> windows = mService.mWindows; + + final WindowList windows = mDisplayContent.getWindowList(); final int N = windows.size(); for (int i = N - 1; i >= 0; i--) { WindowState child = windows.get(i); diff --git a/services/java/com/android/server/wm/FakeWindowImpl.java b/services/java/com/android/server/wm/FakeWindowImpl.java index 2527f46..5ec72cc 100644 --- a/services/java/com/android/server/wm/FakeWindowImpl.java +++ b/services/java/com/android/server/wm/FakeWindowImpl.java @@ -22,6 +22,7 @@ import com.android.server.input.InputWindowHandle; import android.os.Looper; import android.os.Process; import android.util.Slog; +import android.view.Display; import android.view.InputChannel; import android.view.InputEventReceiver; import android.view.InputQueue; @@ -56,7 +57,7 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow { mApplicationHandle.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - mWindowHandle = new InputWindowHandle(mApplicationHandle, null); + mWindowHandle = new InputWindowHandle(mApplicationHandle, null, Display.DEFAULT_DISPLAY); mWindowHandle.name = name; mWindowHandle.inputChannel = mServerChannel; mWindowLayer = getLayerLw(windowType); diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 285d230..5ff8a9b 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -128,7 +128,7 @@ final class InputMonitor implements InputManagerService.Callbacks { return 0; // abort dispatching } - private void addInputWindowHandleLw(InputWindowHandle windowHandle) { + private void addInputWindowHandleLw(final InputWindowHandle windowHandle) { if (mInputWindowHandles == null) { mInputWindowHandles = new InputWindowHandle[16]; } @@ -139,6 +139,44 @@ final class InputMonitor implements InputManagerService.Callbacks { mInputWindowHandles[mInputWindowHandleCount++] = windowHandle; } + private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle, + final WindowState child, final int flags, final int type, + final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper) { + // Add a window to our list of input windows. + inputWindowHandle.name = child.toString(); + inputWindowHandle.layoutParamsFlags = flags; + inputWindowHandle.layoutParamsType = type; + inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); + inputWindowHandle.visible = isVisible; + inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); + inputWindowHandle.hasFocus = hasFocus; + inputWindowHandle.hasWallpaper = hasWallpaper; + inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false; + inputWindowHandle.layer = child.mLayer; + inputWindowHandle.ownerPid = child.mSession.mPid; + inputWindowHandle.ownerUid = child.mSession.mUid; + inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; + + final Rect frame = child.mFrame; + inputWindowHandle.frameLeft = frame.left; + inputWindowHandle.frameTop = frame.top; + inputWindowHandle.frameRight = frame.right; + inputWindowHandle.frameBottom = frame.bottom; + + if (child.mGlobalScale != 1) { + // If we are scaling the window, input coordinates need + // to be inversely scaled to map from what is on screen + // to what is actually being touched in the UI. + inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; + } else { + inputWindowHandle.scaleFactor = 1; + } + + child.getTouchableRegion(inputWindowHandle.touchableRegion); + + addInputWindowHandleLw(inputWindowHandle); + } + private void clearInputWindowHandlesLw() { while (mInputWindowHandleCount != 0) { mInputWindowHandles[--mInputWindowHandleCount] = null; @@ -163,7 +201,9 @@ final class InputMonitor implements InputManagerService.Callbacks { // As an optimization, we could try to prune the list of windows but this turns // out to be difficult because only the native code knows for sure which window // currently has touch focus. - final ArrayList<WindowState> windows = mService.mWindows; + final WindowStateAnimator universeBackground = mService.mAnimator.mUniverseBackground; + final int aboveUniverseLayer = mService.mAnimator.mAboveUniverseLayer; + boolean addedUniverse = false; // If there's a drag in flight, provide a pseudowindow to catch drag input final boolean inDrag = (mService.mDragState != null); @@ -185,8 +225,9 @@ final class InputMonitor implements InputManagerService.Callbacks { addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle); } - final int N = windows.size(); - for (int i = N - 1; i >= 0; i--) { + // TODO(multidisplay): Input only occurs on the default display. + final WindowList windows = mService.getDefaultWindowList(); + for (int i = windows.size() - 1; i >= 0; i--) { final WindowState child = windows.get(i); final InputChannel inputChannel = child.mInputChannel; final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; @@ -209,39 +250,20 @@ final class InputMonitor implements InputManagerService.Callbacks { mService.mDragState.sendDragStartedIfNeededLw(child); } - // Add a window to our list of input windows. - inputWindowHandle.name = child.toString(); - inputWindowHandle.layoutParamsFlags = flags; - inputWindowHandle.layoutParamsType = type; - inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); - inputWindowHandle.visible = isVisible; - inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); - inputWindowHandle.hasFocus = hasFocus; - inputWindowHandle.hasWallpaper = hasWallpaper; - inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false; - inputWindowHandle.layer = child.mLayer; - inputWindowHandle.ownerPid = child.mSession.mPid; - inputWindowHandle.ownerUid = child.mSession.mUid; - inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; - - final Rect frame = child.mFrame; - inputWindowHandle.frameLeft = frame.left; - inputWindowHandle.frameTop = frame.top; - inputWindowHandle.frameRight = frame.right; - inputWindowHandle.frameBottom = frame.bottom; - - if (child.mGlobalScale != 1) { - // If we are scaling the window, input coordinates need - // to be inversely scaled to map from what is on screen - // to what is actually being touched in the UI. - inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; - } else { - inputWindowHandle.scaleFactor = 1; + if (universeBackground != null && !addedUniverse + && child.mBaseLayer < aboveUniverseLayer) { + final WindowState u = universeBackground.mWin; + if (u.mInputChannel != null && u.mInputWindowHandle != null) { + addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags, + u.mAttrs.type, true, u == mInputFocus, false); + } + addedUniverse = true; } - child.getTouchableRegion(inputWindowHandle.touchableRegion); - - addInputWindowHandleLw(inputWindowHandle); + if (child.mWinAnimator != universeBackground) { + addInputWindowHandleLw(inputWindowHandle, child, flags, type, + isVisible, hasFocus, hasWallpaper); + } } // Send windows to native code. diff --git a/services/java/com/android/server/wm/KeyguardDisableHandler.java b/services/java/com/android/server/wm/KeyguardDisableHandler.java new file mode 100644 index 0000000..d935b8b --- /dev/null +++ b/services/java/com/android/server/wm/KeyguardDisableHandler.java @@ -0,0 +1,107 @@ +/* + * 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.wm; + +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.TokenWatcher; +import android.util.Log; +import android.util.Pair; +import android.view.WindowManagerPolicy; + +public class KeyguardDisableHandler extends Handler { + private static final String TAG = "KeyguardDisableHandler"; + + private static final int ALLOW_DISABLE_YES = 1; + private static final int ALLOW_DISABLE_NO = 0; + private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager + private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher + + // Message.what constants + static final int KEYGUARD_DISABLE = 1; + static final int KEYGUARD_REENABLE = 2; + static final int KEYGUARD_POLICY_CHANGED = 3; + + final Context mContext; + final WindowManagerPolicy mPolicy; + KeyguardTokenWatcher mKeyguardTokenWatcher; + + public KeyguardDisableHandler(final Context context, final WindowManagerPolicy policy) { + mContext = context; + mPolicy = policy; + } + + @SuppressWarnings("unchecked") + @Override + public void handleMessage(Message msg) { + if (mKeyguardTokenWatcher == null) { + mKeyguardTokenWatcher = new KeyguardTokenWatcher(this); + } + + switch (msg.what) { + case KEYGUARD_DISABLE: + final Pair<IBinder, String> pair = (Pair<IBinder, String>)msg.obj; + mKeyguardTokenWatcher.acquire(pair.first, pair.second); + break; + + case KEYGUARD_REENABLE: + mKeyguardTokenWatcher.release((IBinder)msg.obj); + break; + + case KEYGUARD_POLICY_CHANGED: + mPolicy.enableKeyguard(true); + // lazily evaluate this next time we're asked to disable keyguard + mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; + break; + } + } + + class KeyguardTokenWatcher extends TokenWatcher { + + public KeyguardTokenWatcher(final Handler handler) { + super(handler, TAG); + } + + @Override + public void acquired() { + // We fail safe and prevent disabling keyguard in the unlikely event this gets + // called before DevicePolicyManagerService has started. + if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) { + DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (dpm != null) { + mAllowDisableKeyguard = dpm.getPasswordQuality(null) + == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ? + ALLOW_DISABLE_YES : ALLOW_DISABLE_NO; + } + } + if (mAllowDisableKeyguard == ALLOW_DISABLE_YES) { + mPolicy.enableKeyguard(false); + } else { + Log.v(TAG, "Not disabling keyguard since device policy is enforced"); + } + } + + @Override + public void released() { + mPolicy.enableKeyguard(true); + } + } +} diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java index 938db9e..7679413 100644 --- a/services/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java @@ -25,6 +25,7 @@ import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Rect; import android.util.Slog; +import android.view.Display; import android.view.Surface; import android.view.SurfaceSession; import android.view.animation.Animation; @@ -41,6 +42,7 @@ class ScreenRotationAnimation { static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200; final Context mContext; + final Display mDisplay; Surface mSurface; BlackFrame mCustomBlackFrame; BlackFrame mExitingBlackFrame; @@ -185,9 +187,10 @@ class ScreenRotationAnimation { pw.println(); } - public ScreenRotationAnimation(Context context, SurfaceSession session, + public ScreenRotationAnimation(Context context, Display display, SurfaceSession session, boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) { mContext = context; + mDisplay = display; // Screenshot does NOT include rotation! if (originalRotation == Surface.ROTATION_90 @@ -212,11 +215,13 @@ class ScreenRotationAnimation { try { try { if (WindowManagerService.DEBUG_SURFACE_TRACE) { - mSurface = new SurfaceTrace(session, 0, "FreezeSurface", -1, mWidth, mHeight, - PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN); + mSurface = new SurfaceTrace(session, 0, "FreezeSurface", + mDisplay.getLayerStack(), mWidth, mHeight, + PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN); } else { - mSurface = new Surface(session, 0, "FreezeSurface", -1, mWidth, mHeight, - PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN); + mSurface = new Surface(session, 0, "FreezeSurface", + mDisplay.getLayerStack(), mWidth, mHeight, + PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN); } if (!mSurface.isValid()) { // Screenshot failed, punt. @@ -472,6 +477,7 @@ class ScreenRotationAnimation { mRotateFrameAnimation.scaleCurrentDuration(animationScale); } + final int layerStack = mDisplay.getLayerStack(); if (USE_CUSTOM_BLACK_FRAME && mCustomBlackFrame == null) { if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i( WindowManagerService.TAG, @@ -490,7 +496,8 @@ class ScreenRotationAnimation { Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1, mOriginalWidth*2, mOriginalHeight*2); Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight); - mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3); + mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3, + layerStack); mCustomBlackFrame.setMatrix(mFrameInitialMatrix); } catch (Surface.OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate black surface", e); @@ -520,7 +527,8 @@ class ScreenRotationAnimation { Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1, mOriginalWidth*2, mOriginalHeight*2); Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight); - mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2); + mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2, + layerStack); mExitingBlackFrame.setMatrix(mFrameInitialMatrix); } catch (Surface.OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate black surface", e); @@ -542,7 +550,8 @@ class ScreenRotationAnimation { Rect outer = new Rect(-finalWidth*1, -finalHeight*1, finalWidth*2, finalHeight*2); Rect inner = new Rect(0, 0, finalWidth, finalHeight); - mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER); + mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER, + layerStack); } catch (Surface.OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate black surface", e); } finally { diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java index 61c0e9c..1ffbecc 100644 --- a/services/java/com/android/server/wm/Session.java +++ b/services/java/com/android/server/wm/Session.java @@ -33,6 +33,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; +import android.view.Display; import android.view.IWindow; import android.view.IWindowSession; import android.view.InputChannel; @@ -134,15 +135,33 @@ final class Session extends IWindowSession.Stub } } + @Override public int add(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { - return mService.addWindow(this, window, seq, attrs, viewVisibility, outContentInsets, - outInputChannel); + return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY, + outContentInsets, outInputChannel); } - + + @Override + public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, + int viewVisibility, int displayId, Rect outContentInsets, + InputChannel outInputChannel) { + return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, + outContentInsets, outInputChannel); + } + + @Override public int addWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets) { - return mService.addWindow(this, window, seq, attrs, viewVisibility, outContentInsets, null); + return addToDisplayWithoutInputChannel(window, seq, attrs, viewVisibility, + Display.DEFAULT_DISPLAY, outContentInsets); + } + + @Override + public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs, + int viewVisibility, int displayId, Rect outContentInsets) { + return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, + outContentInsets, null); } public void remove(IWindow window) { @@ -261,7 +280,7 @@ final class Session extends IWindowSession.Stub // !!! FIXME: put all this heavy stuff onto the mH looper, as well as // the actual drag event dispatch stuff in the dragstate - mService.mDragState.register(); + mService.mDragState.register(callingWin.mDisplayContent); mService.mInputMonitor.updateInputWindowsLw(true /*force*/); if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, mService.mDragState.mServerChannel)) { @@ -389,6 +408,20 @@ final class Session extends IWindowSession.Stub mService.wallpaperCommandComplete(window, result); } + public void setUniverseTransform(IBinder window, float alpha, float offx, float offy, + float dsdx, float dtdx, float dsdy, float dtdy) { + synchronized(mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + mService.setUniverseTransformLocked( + mService.windowForClientLocked(this, window, true), + alpha, offx, offy, dsdx, dtdx, dsdy, dtdy); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + void windowAddedLocked() { if (mSurfaceSession == null) { if (WindowManagerService.localLOGV) Slog.v( diff --git a/services/java/com/android/server/wm/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java index 768d2db..775aa0f 100644 --- a/services/java/com/android/server/wm/StrictModeFlash.java +++ b/services/java/com/android/server/wm/StrictModeFlash.java @@ -22,8 +22,6 @@ import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; -import android.util.DisplayMetrics; -import android.util.Slog; import android.view.Display; import android.view.Surface; import android.view.SurfaceSession; @@ -39,7 +37,8 @@ class StrictModeFlash { public StrictModeFlash(Display display, SurfaceSession session) { try { - mSurface = new Surface(session, 0, "StrictModeFlash", -1, 1, 1, PixelFormat.TRANSLUCENT, 0); + mSurface = new Surface(session, 0, "StrictModeFlash", display.getLayerStack(), + 1, 1, PixelFormat.TRANSLUCENT, 0); } catch (Surface.OutOfResourcesException e) { return; } diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java index 5497eb4..5901cc8 100644 --- a/services/java/com/android/server/wm/Watermark.java +++ b/services/java/com/android/server/wm/Watermark.java @@ -35,6 +35,7 @@ import android.view.Surface.OutOfResourcesException; * Displays a watermark on top of the window manager's windows. */ class Watermark { + final Display mDisplay; final String[] mTokens; final String mText; final Paint mTextPaint; @@ -50,7 +51,7 @@ class Watermark { int mLastDH; boolean mDrawNeeded; - Watermark(DisplayMetrics dm, SurfaceSession session, String[] tokens) { + Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) { if (false) { Log.i(WindowManagerService.TAG, "*********************** WATERMARK"); for (int i=0; i<tokens.length; i++) { @@ -58,6 +59,7 @@ class Watermark { } } + mDisplay = display; mTokens = tokens; StringBuilder builder = new StringBuilder(32); @@ -112,7 +114,8 @@ class Watermark { try { mSurface = new Surface(session, 0, - "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0); + "WatermarkSurface", mDisplay.getLayerStack(), + 1, 1, PixelFormat.TRANSLUCENT, 0); mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100); mSurface.setPosition(0, 0); mSurface.show(); diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java index 62cf711..580f00d 100644 --- a/services/java/com/android/server/wm/WindowAnimator.java +++ b/services/java/com/android/server/wm/WindowAnimator.java @@ -8,19 +8,22 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED; +import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE; -import static com.android.server.wm.WindowManagerService.H.SET_DIM_PARAMETERS; +import static com.android.server.wm.WindowManagerService.H.UPDATE_ANIM_PARAMETERS; import android.content.Context; import android.os.SystemClock; import android.util.Log; import android.util.Slog; +import android.view.Display; import android.view.Surface; -import android.view.WindowManager; import android.view.WindowManagerPolicy; import android.view.animation.Animation; import com.android.internal.policy.impl.PhoneWindowManager; +import com.android.server.wm.WindowManagerService.AppWindowAnimParams; +import com.android.server.wm.WindowManagerService.LayoutToAnimatorParams; import java.io.PrintWriter; import java.util.ArrayList; @@ -32,22 +35,16 @@ import java.util.ArrayList; public class WindowAnimator { private static final String TAG = "WindowAnimator"; - // mForceHiding states. - private static final int KEYGUARD_NOT_SHOWN = 0; - private static final int KEYGUARD_ANIMATING_IN = 1; - private static final int KEYGUARD_SHOWN = 2; - private static final int KEYGUARD_ANIMATING_OUT = 3; - int mForceHiding; - final WindowManagerService mService; final Context mContext; final WindowManagerPolicy mPolicy; - ArrayList<WindowStateAnimator> mWinAnimators = new ArrayList<WindowStateAnimator>(); + ArrayList<WinAnimatorList> mWinAnimatorLists = new ArrayList<WinAnimatorList>(); boolean mAnimating; - WindowState mWindowAnimationBackground; - int mWindowAnimationBackgroundColor; + + final Runnable mAnimationRunnable; + int mAdjResult; int mPendingLayoutChanges; @@ -71,11 +68,14 @@ public class WindowAnimator { // Window currently running an animation that has requested it be detached // from the wallpaper. This means we need to ensure the wallpaper is // visible behind it in case it animates in a way that would allow it to be - // seen. + // seen. If multiple windows satisfy this, use the lowest window. WindowState mWindowDetachedWallpaper = null; - WindowState mDetachedWallpaper = null; + DimSurface mWindowAnimationBackgroundSurface = null; + WindowStateAnimator mUniverseBackground = null; + int mAboveUniverseLayer = 0; + int mBulkUpdateParams = 0; DimAnimator mDimAnimator = null; @@ -84,75 +84,149 @@ public class WindowAnimator { static final int WALLPAPER_ACTION_PENDING = 1; int mPendingActions; - WindowAnimator(final WindowManagerService service, final Context context, - final WindowManagerPolicy policy) { - mService = service; - mContext = context; - mPolicy = policy; + WindowState mWallpaperTarget = null; + AppWindowAnimator mWpAppAnimator = null; + WindowState mLowerWallpaperTarget = null; + WindowState mUpperWallpaperTarget = null; + + ArrayList<AppWindowAnimator> mAppAnimators = new ArrayList<AppWindowAnimator>(); + + ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>(); + + /** Parameters being passed from this into mService. */ + static class AnimatorToLayoutParams { + boolean mUpdateQueued; + int mBulkUpdateParams; + int mPendingLayoutChanges; + WindowState mWindowDetachedWallpaper; } + /** Do not modify unless holding mService.mWindowMap or this and mAnimToLayout in that order */ + final AnimatorToLayoutParams mAnimToLayout = new AnimatorToLayoutParams(); - void hideWallpapersLocked(final WindowState w) { - if ((mService.mWallpaperTarget == w && mService.mLowerWallpaperTarget == null) - || mService.mWallpaperTarget == null) { - for (final WindowToken token : mService.mWallpaperTokens) { - for (final WindowState wallpaper : token.windows) { - final WindowStateAnimator winAnimator = wallpaper.mWinAnimator; - if (!winAnimator.mLastHidden) { - winAnimator.hide(); - mService.dispatchWallpaperVisibility(wallpaper, false); - mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + boolean mInitialized = false; + + WindowAnimator(final WindowManagerService service) { + mService = service; + mContext = service.mContext; + mPolicy = service.mPolicy; + + mAnimationRunnable = new Runnable() { + @Override + public void run() { + // TODO(cmautner): When full isolation is achieved for animation, the first lock + // goes away and only the WindowAnimator.this remains. + synchronized(mService.mWindowMap) { + synchronized(WindowAnimator.this) { + copyLayoutToAnimParamsLocked(); + animateLocked(); } } - token.hidden = true; + } + }; + } + + void initializeLocked(final int layerStack) { + mWindowAnimationBackgroundSurface = + new DimSurface(mService.mFxSession, layerStack); + mDimAnimator = new DimAnimator(mService.mFxSession, layerStack); + mInitialized = true; + } + + /** Locked on mAnimToLayout */ + void updateAnimToLayoutLocked() { + final AnimatorToLayoutParams animToLayout = mAnimToLayout; + synchronized (animToLayout) { + animToLayout.mBulkUpdateParams = mBulkUpdateParams; + animToLayout.mPendingLayoutChanges = mPendingLayoutChanges; + animToLayout.mWindowDetachedWallpaper = mWindowDetachedWallpaper; + + if (!animToLayout.mUpdateQueued) { + animToLayout.mUpdateQueued = true; + mService.mH.sendMessage(mService.mH.obtainMessage(UPDATE_ANIM_PARAMETERS)); } } } - private void testWallpaperAndBackgroundLocked() { - if (mWindowDetachedWallpaper != mDetachedWallpaper) { - if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG, - "Detached wallpaper changed from " + mWindowDetachedWallpaper - + " to " + mDetachedWallpaper); - mWindowDetachedWallpaper = mDetachedWallpaper; - mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE; + /** Copy all WindowManagerService params into local params here. Locked on 'this'. */ + private void copyLayoutToAnimParamsLocked() { + final LayoutToAnimatorParams layoutToAnim = mService.mLayoutToAnim; + synchronized(layoutToAnim) { + layoutToAnim.mAnimationScheduled = false; + + if (!layoutToAnim.mParamsModified) { + return; + } + layoutToAnim.mParamsModified = false; + + if ((layoutToAnim.mChanges & LayoutToAnimatorParams.WALLPAPER_TOKENS_CHANGED) != 0) { + layoutToAnim.mChanges &= ~LayoutToAnimatorParams.WALLPAPER_TOKENS_CHANGED; + mWallpaperTokens = new ArrayList<WindowToken>(layoutToAnim.mWallpaperTokens); + } + + mWinAnimatorLists = + new ArrayList<WinAnimatorList>(layoutToAnim.mWinAnimatorLists); + mWallpaperTarget = layoutToAnim.mWallpaperTarget; + mWpAppAnimator = mWallpaperTarget == null + ? null : mWallpaperTarget.mAppToken == null + ? null : mWallpaperTarget.mAppToken.mAppAnimator; + mLowerWallpaperTarget = layoutToAnim.mLowerWallpaperTarget; + mUpperWallpaperTarget = layoutToAnim.mUpperWallpaperTarget; + + // Set the new DimAnimator params. + DimAnimator.Parameters dimParams = layoutToAnim.mDimParams; + if (dimParams == null) { + mDimParams = dimParams; + } else { + final WindowStateAnimator newWinAnimator = dimParams.mDimWinAnimator; + + // Only set dim params on the highest dimmed layer. + final WindowStateAnimator existingDimWinAnimator = mDimParams == null + ? null : mDimParams.mDimWinAnimator; + // Don't turn on for an unshown surface, or for any layer but the highest dimmed one. + if (newWinAnimator.mSurfaceShown && + (existingDimWinAnimator == null || !existingDimWinAnimator.mSurfaceShown + || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) { + mDimParams = dimParams; + } + } + + mAppAnimators.clear(); + final int N = layoutToAnim.mAppWindowAnimParams.size(); + for (int i = 0; i < N; i++) { + final AppWindowAnimParams params = layoutToAnim.mAppWindowAnimParams.get(i); + AppWindowAnimator appAnimator = params.mAppAnimator; + appAnimator.mAllAppWinAnimators.clear(); + appAnimator.mAllAppWinAnimators.addAll(params.mWinAnimators); + mAppAnimators.add(appAnimator); + } } + } - if (mWindowAnimationBackgroundColor != 0) { - // If the window that wants black is the current wallpaper - // target, then the black goes *below* the wallpaper so we - // don't cause the wallpaper to suddenly disappear. - WindowState target = mWindowAnimationBackground; - if (mService.mWallpaperTarget == target - || mService.mLowerWallpaperTarget == target - || mService.mUpperWallpaperTarget == target) { - final int N = mService.mWindows.size(); - for (int i = 0; i < N; i++) { - WindowState w = mService.mWindows.get(i); - if (w.mIsWallpaper) { - target = w; - break; + void hideWallpapersLocked(final WindowState w) { + if ((mWallpaperTarget == w && mLowerWallpaperTarget == null) || mWallpaperTarget == null) { + final int numTokens = mWallpaperTokens.size(); + for (int i = numTokens - 1; i >= 0; i--) { + final WindowToken token = mWallpaperTokens.get(i); + final int numWindows = token.windows.size(); + for (int j = numWindows - 1; j >= 0; j--) { + final WindowState wallpaper = token.windows.get(j); + final WindowStateAnimator winAnimator = wallpaper.mWinAnimator; + if (!winAnimator.mLastHidden) { + winAnimator.hide(); + mService.dispatchWallpaperVisibility(wallpaper, false); + mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } } + token.hidden = true; } - if (mWindowAnimationBackgroundSurface == null) { - mWindowAnimationBackgroundSurface = new DimSurface(mService.mFxSession); - } - final int dw = mDw; - final int dh = mDh; - mWindowAnimationBackgroundSurface.show(dw, dh, - target.mWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM, - mWindowAnimationBackgroundColor); - } else if (mWindowAnimationBackgroundSurface != null) { - mWindowAnimationBackgroundSurface.hide(); } } private void updateWindowsAppsAndRotationAnimationsLocked() { - final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens; int i; - final int NAT = appTokens.size(); + final int NAT = mAppAnimators.size(); for (i=0; i<NAT; i++) { - final AppWindowAnimator appAnimator = appTokens.get(i).mAppAnimator; + final AppWindowAnimator appAnimator = mAppAnimators.get(i); final boolean wasAnimating = appAnimator.animation != null && appAnimator.animation != AppWindowAnimator.sDummyAnimation; if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) { @@ -199,15 +273,22 @@ public class WindowAnimator { } } - private void updateWindowsAndWallpaperLocked() { + private void updateWindowsLocked(final WinAnimatorList winAnimatorList) { ++mAnimTransactionSequence; ArrayList<WindowStateAnimator> unForceHiding = null; boolean wallpaperInUnForceHiding = false; - for (int i = mService.mWindows.size() - 1; i >= 0; i--) { - WindowState win = mService.mWindows.get(i); - WindowStateAnimator winAnimator = win.mWinAnimator; + // forceHiding states. + final int KEYGUARD_NOT_SHOWN = 0; + final int KEYGUARD_ANIMATING_IN = 1; + final int KEYGUARD_SHOWN = 2; + final int KEYGUARD_ANIMATING_OUT = 3; + int forceHiding = KEYGUARD_NOT_SHOWN; + + for (int i = winAnimatorList.size() - 1; i >= 0; i--) { + WindowStateAnimator winAnimator = winAnimatorList.get(i); + WindowState win = winAnimator.mWin; final int flags = winAnimator.mAttrFlags; if (winAnimator.mSurface != null) { @@ -219,51 +300,7 @@ public class WindowAnimator { ", nowAnimating=" + nowAnimating); } - // If this window is animating, make a note that we have - // an animating window and take care of a request to run - // a detached wallpaper animation. - if (nowAnimating) { - if (winAnimator.mAnimation != null) { - if ((flags & FLAG_SHOW_WALLPAPER) != 0 - && winAnimator.mAnimation.getDetachWallpaper()) { - mDetachedWallpaper = win; - } - final int backgroundColor = winAnimator.mAnimation.getBackgroundColor(); - if (backgroundColor != 0) { - if (mWindowAnimationBackground == null - || (winAnimator.mAnimLayer < - mWindowAnimationBackground.mWinAnimator.mAnimLayer)) { - mWindowAnimationBackground = win; - mWindowAnimationBackgroundColor = backgroundColor; - } - } - } - mAnimating = true; - } - - // If this window's app token is running a detached wallpaper - // animation, make a note so we can ensure the wallpaper is - // displayed behind it. - final AppWindowAnimator appAnimator = - win.mAppToken == null ? null : win.mAppToken.mAppAnimator; - if (appAnimator != null && appAnimator.animation != null - && appAnimator.animating) { - if ((flags & FLAG_SHOW_WALLPAPER) != 0 - && appAnimator.animation.getDetachWallpaper()) { - mDetachedWallpaper = win; - } - final int backgroundColor = appAnimator.animation.getBackgroundColor(); - if (backgroundColor != 0) { - if (mWindowAnimationBackground == null - || (winAnimator.mAnimLayer < - mWindowAnimationBackground.mWinAnimator.mAnimLayer)) { - mWindowAnimationBackground = win; - mWindowAnimationBackgroundColor = backgroundColor; - } - } - } - - if (wasAnimating && !winAnimator.mAnimating && mService.mWallpaperTarget == win) { + if (wasAnimating && !winAnimator.mAnimating && mWallpaperTarget == win) { mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE; mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; if (WindowManagerService.DEBUG_LAYOUT_REPEATS) { @@ -288,16 +325,16 @@ public class WindowAnimator { if (win.isReadyForDisplay()) { if (nowAnimating) { if (winAnimator.mAnimationIsEntrance) { - mForceHiding = KEYGUARD_ANIMATING_IN; + forceHiding = KEYGUARD_ANIMATING_IN; } else { - mForceHiding = KEYGUARD_ANIMATING_OUT; + forceHiding = KEYGUARD_ANIMATING_OUT; } } else { - mForceHiding = KEYGUARD_SHOWN; + forceHiding = KEYGUARD_SHOWN; } } if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, - "Force hide " + mForceHiding + "Force hide " + forceHiding + " hasSurface=" + win.mHasSurface + " policyVis=" + win.mPolicyVisibility + " destroying=" + win.mDestroying @@ -309,9 +346,9 @@ public class WindowAnimator { final boolean hideWhenLocked = (winAnimator.mAttrFlags & FLAG_SHOW_WHEN_LOCKED) == 0; final boolean changed; - if (((mForceHiding == KEYGUARD_ANIMATING_IN) + if (((forceHiding == KEYGUARD_ANIMATING_IN) && (!winAnimator.isAnimating() || hideWhenLocked)) - || ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) { + || ((forceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) { changed = win.hideLw(false, false); if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG, "Now policy hidden: " + win); @@ -326,7 +363,7 @@ public class WindowAnimator { unForceHiding = new ArrayList<WindowStateAnimator>(); } unForceHiding.add(winAnimator); - if ((win.mAttrs.flags&WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) { + if ((flags & FLAG_SHOW_WALLPAPER) != 0) { wallpaperInUnForceHiding = true; } } @@ -338,7 +375,7 @@ public class WindowAnimator { } } } - if (changed && (flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) { + if (changed && (flags & FLAG_SHOW_WALLPAPER) != 0) { mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE; mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; if (WindowManagerService.DEBUG_LAYOUT_REPEATS) { @@ -361,8 +398,7 @@ public class WindowAnimator { } } } - final AppWindowAnimator appAnimator = - atoken == null ? null : atoken.mAppAnimator; + final AppWindowAnimator appAnimator = winAnimator.mAppAnimator; if (appAnimator != null && appAnimator.thumbnail != null) { if (appAnimator.thumbnailTransactionSeq != mAnimTransactionSequence) { appAnimator.thumbnailTransactionSeq = mAnimTransactionSequence; @@ -388,21 +424,112 @@ public class WindowAnimator { } } + private void updateWallpaperLocked(final WinAnimatorList winAnimatorList) { + WindowStateAnimator windowAnimationBackground = null; + int windowAnimationBackgroundColor = 0; + WindowState detachedWallpaper = null; + + for (int i = winAnimatorList.size() - 1; i >= 0; i--) { + WindowStateAnimator winAnimator = winAnimatorList.get(i); + if (winAnimator.mSurface == null) { + continue; + } + + final int flags = winAnimator.mAttrFlags; + final WindowState win = winAnimator.mWin; + + // If this window is animating, make a note that we have + // an animating window and take care of a request to run + // a detached wallpaper animation. + if (winAnimator.mAnimating) { + if (winAnimator.mAnimation != null) { + if ((flags & FLAG_SHOW_WALLPAPER) != 0 + && winAnimator.mAnimation.getDetachWallpaper()) { + detachedWallpaper = win; + } + final int backgroundColor = winAnimator.mAnimation.getBackgroundColor(); + if (backgroundColor != 0) { + if (windowAnimationBackground == null || (winAnimator.mAnimLayer < + windowAnimationBackground.mAnimLayer)) { + windowAnimationBackground = winAnimator; + windowAnimationBackgroundColor = backgroundColor; + } + } + } + mAnimating = true; + } + + // If this window's app token is running a detached wallpaper + // animation, make a note so we can ensure the wallpaper is + // displayed behind it. + final AppWindowAnimator appAnimator = winAnimator.mAppAnimator; + if (appAnimator != null && appAnimator.animation != null + && appAnimator.animating) { + if ((flags & FLAG_SHOW_WALLPAPER) != 0 + && appAnimator.animation.getDetachWallpaper()) { + detachedWallpaper = win; + } + + final int backgroundColor = appAnimator.animation.getBackgroundColor(); + if (backgroundColor != 0) { + if (windowAnimationBackground == null || (winAnimator.mAnimLayer < + windowAnimationBackground.mAnimLayer)) { + windowAnimationBackground = winAnimator; + windowAnimationBackgroundColor = backgroundColor; + } + } + } + } // end forall windows + + if (mWindowDetachedWallpaper != detachedWallpaper) { + if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG, + "Detached wallpaper changed from " + mWindowDetachedWallpaper + + " to " + detachedWallpaper); + mWindowDetachedWallpaper = detachedWallpaper; + mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE; + } + + if (windowAnimationBackgroundColor != 0) { + // If the window that wants black is the current wallpaper + // target, then the black goes *below* the wallpaper so we + // don't cause the wallpaper to suddenly disappear. + int animLayer = windowAnimationBackground.mAnimLayer; + WindowState win = windowAnimationBackground.mWin; + if (mWallpaperTarget == win + || mLowerWallpaperTarget == win || mUpperWallpaperTarget == win) { + final int N = winAnimatorList.size(); + for (int i = 0; i < N; i++) { + WindowStateAnimator winAnimator = winAnimatorList.get(i); + if (winAnimator.mIsWallpaper) { + animLayer = winAnimator.mAnimLayer; + break; + } + } + } + + mWindowAnimationBackgroundSurface.show(mDw, mDh, + animLayer - WindowManagerService.LAYER_OFFSET_DIM, + windowAnimationBackgroundColor); + } else { + mWindowAnimationBackgroundSurface.hide(); + } + } + private void testTokenMayBeDrawnLocked() { // See if any windows have been drawn, so they (and others // associated with them) can now be shown. - final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens; - final int NT = appTokens.size(); + final int NT = mAppAnimators.size(); for (int i=0; i<NT; i++) { - AppWindowToken wtoken = appTokens.get(i); + AppWindowAnimator appAnimator = mAppAnimators.get(i); + AppWindowToken wtoken = appAnimator.mAppToken; final boolean allDrawn = wtoken.allDrawn; - if (allDrawn != wtoken.mAppAnimator.allDrawn) { - wtoken.mAppAnimator.allDrawn = allDrawn; + if (allDrawn != appAnimator.allDrawn) { + appAnimator.allDrawn = allDrawn; if (allDrawn) { // The token has now changed state to having all // windows shown... what to do, what to do? - if (wtoken.mAppAnimator.freezingScreen) { - wtoken.mAppAnimator.showAllWindowsLocked(); + if (appAnimator.freezingScreen) { + appAnimator.showAllWindowsLocked(); mService.unsetAppFreezingScreenLocked(wtoken, false, true); if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG, "Setting mOrientationChangeComplete=true because wtoken " @@ -419,7 +546,7 @@ public class WindowAnimator { // We can now show all of the drawn windows! if (!mService.mOpeningApps.contains(wtoken)) { - mAnimating |= wtoken.mAppAnimator.showAllWindowsLocked(); + mAnimating |= appAnimator.showAllWindowsLocked(); } } } @@ -427,13 +554,10 @@ public class WindowAnimator { } } - private void performAnimationsLocked() { - mForceHiding = KEYGUARD_NOT_SHOWN; - mDetachedWallpaper = null; - mWindowAnimationBackground = null; - mWindowAnimationBackgroundColor = 0; + private void performAnimationsLocked(final WinAnimatorList winAnimatorList) { + updateWindowsLocked(winAnimatorList); + updateWallpaperLocked(winAnimatorList); - updateWindowsAndWallpaperLocked(); if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) { mPendingActions |= WALLPAPER_ACTION_PENDING; } @@ -441,10 +565,21 @@ public class WindowAnimator { testTokenMayBeDrawnLocked(); } - synchronized void animate() { + // TODO(cmautner): Change the following comment when no longer locked on mWindowMap */ + /** Locked on mService.mWindowMap and this. */ + private void animateLocked() { + if (!mInitialized) { + return; + } + for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) { + animateLocked(mWinAnimatorLists.get(i)); + } + } + + private void animateLocked(final WinAnimatorList winAnimatorList) { mPendingLayoutChanges = 0; mCurrentTime = SystemClock.uptimeMillis(); - mBulkUpdateParams = 0; + mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE; boolean wasAnimating = mAnimating; mAnimating = false; if (WindowManagerService.DEBUG_WINDOW_TRACE) { @@ -457,8 +592,7 @@ public class WindowAnimator { try { updateWindowsAppsAndRotationAnimationsLocked(); - performAnimationsLocked(); - testWallpaperAndBackgroundLocked(); + performAnimationsLocked(winAnimatorList); // THIRD LOOP: Update the surfaces of all windows. @@ -466,9 +600,9 @@ public class WindowAnimator { mScreenRotationAnimation.updateSurfaces(); } - final int N = mWinAnimators.size(); + final int N = winAnimatorList.size(); for (int i = 0; i < N; i++) { - mWinAnimators.get(i).prepareSurfaceLocked(true); + winAnimatorList.get(i).prepareSurfaceLocked(true); } if (mDimParams != null) { @@ -497,10 +631,14 @@ public class WindowAnimator { Surface.closeTransaction(); } - mService.bulkSetParameters(mBulkUpdateParams, mPendingLayoutChanges); + if (mBulkUpdateParams != 0 || mPendingLayoutChanges != 0) { + updateAnimToLayoutLocked(); + } if (mAnimating) { - mService.scheduleAnimationLocked(); + synchronized (mService.mLayoutToAnim) { + mService.scheduleAnimationLocked(); + } } else if (wasAnimating) { mService.requestTraversalLocked(); } @@ -524,28 +662,6 @@ public class WindowAnimator { mInnerDh = appHeight; } - void startDimming(final WindowStateAnimator winAnimator, final float target, - final int width, final int height) { - if (mDimAnimator == null) { - mDimAnimator = new DimAnimator(mService.mFxSession); - } - // Only set dim params on the highest dimmed layer. - final WindowStateAnimator dimWinAnimator = mDimParams == null - ? null : mDimParams.mDimWinAnimator; - // Don't turn on for an unshown surface, or for any layer but the highest dimmed one. - if (winAnimator.mSurfaceShown && - (dimWinAnimator == null || !dimWinAnimator.mSurfaceShown - || dimWinAnimator.mAnimLayer < winAnimator.mAnimLayer)) { - mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS, - new DimAnimator.Parameters(winAnimator, width, height, target))); - } - } - - // TODO(cmautner): Move into Handler - void stopDimming() { - mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS, null)); - } - boolean isDimming() { return mDimParams != null; } diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 7011343..42bc7ce 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 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_UNIVERSE_BACKGROUND; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import com.android.internal.app.IBatteryStats; @@ -42,12 +43,13 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.WindowManagerPolicyThread; import com.android.server.AttributeCache; import com.android.server.EventLogTags; -import com.android.server.PowerManagerService; import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; -import com.android.server.input.InputFilter; +import com.android.server.display.DisplayDeviceInfo; +import com.android.server.display.DisplayManagerService; import com.android.server.input.InputManagerService; -import com.android.server.pm.ShutdownThread; +import com.android.server.power.PowerManagerService; +import com.android.server.power.ShutdownThread; import android.Manifest; import android.app.ActivityManagerNative; @@ -55,6 +57,7 @@ import android.app.ActivityOptions; import android.app.IActivityManager; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; +import android.animation.ValueAnimator; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -69,15 +72,15 @@ import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Region; -import android.os.BatteryStats; +import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; -import android.os.LocalPowerManager; import android.os.Looper; import android.os.Message; import android.os.Parcel; @@ -89,22 +92,25 @@ import android.os.ServiceManager; import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.TokenWatcher; import android.os.Trace; +import android.os.WorkSource; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.FloatMath; import android.util.Log; -import android.util.LogPrinter; +import android.util.SparseArray; +//import android.util.LogPrinter; import android.util.Pair; import android.util.Slog; import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Choreographer; import android.view.Display; +import android.view.DisplayInfo; import android.view.Gravity; import android.view.IApplicationToken; +import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindow; @@ -119,8 +125,9 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; +import android.view.ViewTreeObserver; import android.view.WindowManager; -import android.view.WindowManagerImpl; +import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerPolicy.FakeWindow; @@ -131,6 +138,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.ScaleAnimation; +import android.view.animation.Transformation; import java.io.BufferedWriter; import java.io.DataInputStream; @@ -151,6 +159,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub @@ -267,58 +276,31 @@ public class WindowManagerService extends IWindowManager.Stub private static final String SYSTEM_SECURE = "ro.secure"; private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; - private static final String SYSTEM_HEADLESS = "ro.config.headless"; - /** - * Condition waited on by {@link #reenableKeyguard} to know the call to - * the window policy has finished. - * This is set to true only if mKeyguardTokenWatcher.acquired() has - * actually disabled the keyguard. - */ - private boolean mKeyguardDisabled = false; + final private KeyguardDisableHandler mKeyguardDisableHandler; private final boolean mHeadless; - private static final int ALLOW_DISABLE_YES = 1; - private static final int ALLOW_DISABLE_NO = 0; - private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager - private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher - private static final float THUMBNAIL_ANIMATION_DECELERATE_FACTOR = 1.5f; - final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher( - new Handler(), "WindowManagerService.mKeyguardTokenWatcher") { - @Override - public void acquired() { - if (shouldAllowDisableKeyguard()) { - mPolicy.enableKeyguard(false); - mKeyguardDisabled = true; - } else { - Log.v(TAG, "Not disabling keyguard since device policy is enforced"); - } - } - @Override - public void released() { - mPolicy.enableKeyguard(true); - synchronized (mKeyguardTokenWatcher) { - mKeyguardDisabled = false; - mKeyguardTokenWatcher.notifyAll(); - } - } - }; - final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - mPolicy.enableKeyguard(true); - synchronized(mKeyguardTokenWatcher) { - // lazily evaluate this next time we're asked to disable keyguard - mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; - mKeyguardDisabled = false; + final String action = intent.getAction(); + if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) { + mKeyguardDisableHandler.sendEmptyMessage( + KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED); + } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { + final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); + Slog.v(TAG, "Switching user from " + mCurrentUserId + " to " + newUserId); + mCurrentUserId = newUserId; } } }; + // Current user when multi-user is enabled. Don't show windows of non-current user. + int mCurrentUserId; + final Context mContext; final boolean mHaveInputMethods; @@ -383,11 +365,6 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>(); /** - * Z-ordered (bottom-most first) list of all Window objects. - */ - final ArrayList<WindowState> mWindows = new ArrayList<WindowState>(); - - /** * Fake windows added to the window manager. Note: ordered from top to * bottom, opposite of mWindows. */ @@ -463,29 +440,8 @@ public class WindowManagerService extends IWindowManager.Stub String mLastANRState; - // This protects the following display size properties, so that - // getDisplaySize() doesn't need to acquire the global lock. This is - // needed because the window manager sometimes needs to use ActivityThread - // while it has its global state locked (for example to load animation - // resources), but the ActivityThread also needs get the current display - // size sometimes when it has its package lock held. - // - // These will only be modified with both mWindowMap and mDisplaySizeLock - // held (in that order) so the window manager doesn't need to acquire this - // lock when needing these values in its normal operation. - final Object mDisplaySizeLock = new Object(); - int mInitialDisplayWidth = 0; - int mInitialDisplayHeight = 0; - int mBaseDisplayWidth = 0; - int mBaseDisplayHeight = 0; - int mCurDisplayWidth = 0; - int mCurDisplayHeight = 0; - int mAppDisplayWidth = 0; - int mAppDisplayHeight = 0; - int mSmallestDisplayWidth = 0; - int mSmallestDisplayHeight = 0; - int mLargestDisplayWidth = 0; - int mLargestDisplayHeight = 0; + /** All DisplayDontents in the world, kept here */ + private SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(); int mRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -496,6 +452,7 @@ public class WindowManagerService extends IWindowManager.Stub final Rect mSystemDecorRect = new Rect(); int mSystemDecorLayer = 0; + final Rect mScreenRect = new Rect(); int mPendingLayoutChanges = 0; boolean mLayoutNeeded = true; @@ -512,9 +469,9 @@ public class WindowManagerService extends IWindowManager.Stub // State while inside of layoutAndPlaceSurfacesLocked(). boolean mFocusMayChange; - + Configuration mCurConfiguration = new Configuration(); - + // This is held as long as we have the screen frozen, to give us time to // perform a rotation animation when turning off shows the lock screen which // changes the orientation. @@ -529,7 +486,8 @@ public class WindowManagerService extends IWindowManager.Stub int mNextAppTransitionType = ActivityOptions.ANIM_NONE; String mNextAppTransitionPackage; Bitmap mNextAppTransitionThumbnail; - boolean mNextAppTransitionDelayed; + // Used for thumbnail transitions. True if we're scaling up, false if scaling down + boolean mNextAppTransitionScaleUp; IRemoteCallback mNextAppTransitionCallback; int mNextAppTransitionEnter; int mNextAppTransitionExit; @@ -545,7 +503,7 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>(); final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>(); - Display mDisplay; + Display mDefaultDisplay; boolean mIsTouchDevice; @@ -583,10 +541,10 @@ public class WindowManagerService extends IWindowManager.Stub WindowState mWallpaperTarget = null; // If non-null, we are in the middle of animating from one wallpaper target // to another, and this is the lower one in Z-order. - WindowState mLowerWallpaperTarget = null; + private WindowState mLowerWallpaperTarget = null; // If non-null, we are in the middle of animating from one wallpaper target // to another, and this is the higher one in Z-order. - WindowState mUpperWallpaperTarget = null; + private WindowState mUpperWallpaperTarget = null; int mWallpaperAnimLayerAdjustment; float mLastWallpaperX = -1; float mLastWallpaperY = -1; @@ -611,6 +569,8 @@ public class WindowManagerService extends IWindowManager.Stub float mAnimatorDurationScale = 1.0f; final InputManagerService mInputManager; + final DisplayManagerService mDisplayManagerService; + final DisplayManager mDisplayManager; // Who is holding the screen on. Session mHoldingScreenOn; @@ -626,7 +586,7 @@ public class WindowManagerService extends IWindowManager.Stub static final int SET_UPDATE_ROTATION = 1 << 0; static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1; static final int SET_FORCE_HIDING_CHANGED = 1 << 2; - static final int CLEAR_ORIENTATION_CHANGE_COMPLETE = 1 << 3; + static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3; static final int SET_TURN_ON_SCREEN = 1 << 4; boolean mWallpaperForceHidingChanged = false; @@ -641,7 +601,45 @@ public class WindowManagerService extends IWindowManager.Stub private float mButtonBrightness = -1; private boolean mUpdateRotation = false; } - LayoutFields mInnerFields = new LayoutFields(); + final LayoutFields mInnerFields = new LayoutFields(); + + static class AppWindowAnimParams { + AppWindowAnimator mAppAnimator; + ArrayList<WindowStateAnimator> mWinAnimators; + + public AppWindowAnimParams(final AppWindowAnimator appAnimator) { + mAppAnimator = appAnimator; + + final AppWindowToken wtoken = appAnimator.mAppToken; + mWinAnimators = new ArrayList<WindowStateAnimator>(); + final int N = wtoken.allAppWindows.size(); + for (int i = 0; i < N; i++) { + mWinAnimators.add(wtoken.allAppWindows.get(i).mWinAnimator); + } + } + } + + static class LayoutToAnimatorParams { + boolean mParamsModified; + + static final long WALLPAPER_TOKENS_CHANGED = 1 << 0; + long mChanges; + + boolean mAnimationScheduled; + ArrayList<WinAnimatorList> mWinAnimatorLists = new ArrayList<WinAnimatorList>(); + WindowState mWallpaperTarget; + WindowState mLowerWallpaperTarget; + WindowState mUpperWallpaperTarget; + DimAnimator.Parameters mDimParams; + ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>(); + ArrayList<AppWindowAnimParams> mAppWindowAnimParams = new ArrayList<AppWindowAnimParams>(); + } + /** Params from WindowManagerService to WindowAnimator. Do not modify or read without first + * locking on either mWindowMap or mAnimator and then on mLayoutToAnim */ + final LayoutToAnimatorParams mLayoutToAnim = new LayoutToAnimatorParams(); + + /** The lowest wallpaper target with a detached wallpaper animation on it. */ + WindowState mWindowDetachedWallpaper = null; /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ @@ -650,33 +648,6 @@ public class WindowManagerService extends IWindowManager.Stub /** Only do a maximum of 6 repeated layouts. After that quit */ private int mLayoutRepeatCount; - private final class AnimationRunnable implements Runnable { - @Override - public void run() { - synchronized(mWindowMap) { - mAnimationScheduled = false; - // Update animations of all applications, including those - // associated with exiting/removed apps - synchronized (mAnimator) { - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmAnimate"); - final ArrayList<WindowStateAnimator> winAnimators = mAnimator.mWinAnimators; - winAnimators.clear(); - final int N = mWindows.size(); - for (int i = 0; i < N; i++) { - final WindowStateAnimator winAnimator = mWindows.get(i).mWinAnimator; - if (winAnimator.mSurface != null) { - winAnimators.add(winAnimator); - } - } - mAnimator.animate(); - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - } - } - } - } - final AnimationRunnable mAnimationRunnable = new AnimationRunnable(); - boolean mAnimationScheduled; - final WindowAnimator mAnimator; final class DragInputEventReceiver extends InputEventReceiver { @@ -769,9 +740,10 @@ public class WindowManagerService extends IWindowManager.Stub final boolean mOnlyCore; public static WindowManagerService main(Context context, - PowerManagerService pm, boolean haveInputMethods, boolean allowBootMsgs, + PowerManagerService pm, DisplayManagerService dm, + boolean haveInputMethods, boolean allowBootMsgs, boolean onlyCore) { - WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs, onlyCore); + WMThread thr = new WMThread(context, pm, dm, haveInputMethods, allowBootMsgs, onlyCore); thr.start(); synchronized (thr) { @@ -790,15 +762,18 @@ public class WindowManagerService extends IWindowManager.Stub private final Context mContext; private final PowerManagerService mPM; + private final DisplayManagerService mDisplayManager; private final boolean mHaveInputMethods; private final boolean mAllowBootMessages; private final boolean mOnlyCore; public WMThread(Context context, PowerManagerService pm, + DisplayManagerService dm, boolean haveInputMethods, boolean allowBootMsgs, boolean onlyCore) { super("WindowManager"); mContext = context; mPM = pm; + mDisplayManager = dm; mHaveInputMethods = haveInputMethods; mAllowBootMessages = allowBootMsgs; mOnlyCore = onlyCore; @@ -809,7 +784,7 @@ public class WindowManagerService extends IWindowManager.Stub Looper.prepare(); //Looper.myLooper().setMessageLogging(new LogPrinter( // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM)); - WindowManagerService s = new WindowManagerService(mContext, mPM, + WindowManagerService s = new WindowManagerService(mContext, mPM, mDisplayManager, mHaveInputMethods, mAllowBootMessages, mOnlyCore); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_DISPLAY); @@ -833,30 +808,30 @@ public class WindowManagerService extends IWindowManager.Stub private final WindowManagerPolicy mPolicy; private final WindowManagerService mService; private final Context mContext; - private final PowerManagerService mPM; boolean mRunning = false; public PolicyThread(WindowManagerPolicy policy, - WindowManagerService service, Context context, - PowerManagerService pm) { + WindowManagerService service, Context context) { super("WindowManagerPolicy"); mPolicy = policy; mService = service; mContext = context; - mPM = pm; } @Override public void run() { Looper.prepare(); WindowManagerPolicyThread.set(this, Looper.myLooper()); - + //Looper.myLooper().setMessageLogging(new LogPrinter( // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM)); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); android.os.Process.setCanSelfBackground(false); - mPolicy.init(mContext, mService, mService, mPM); + mPolicy.init(mContext, mService, mService); + mService.mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer() + * TYPE_LAYER_MULTIPLIER + + TYPE_LAYER_OFFSET; synchronized (this) { mRunning = true; @@ -873,6 +848,7 @@ public class WindowManagerService extends IWindowManager.Stub } private WindowManagerService(Context context, PowerManagerService pm, + DisplayManagerService displayManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { mContext = context; mHaveInputMethods = haveInputMethods; @@ -880,7 +856,11 @@ public class WindowManagerService extends IWindowManager.Stub mOnlyCore = onlyCore; mLimitedAlphaCompositing = context.getResources().getBoolean( com.android.internal.R.bool.config_sf_limitedAlpha); - mHeadless = "1".equals(SystemProperties.get(SYSTEM_HEADLESS, "0")); + mDisplayManagerService = displayManager; + mDisplayManager = DisplayManager.getInstance(); + mHeadless = displayManager.isHeadless(); + + mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); mPowerManager = pm; mPowerManager.setPolicy(mPolicy); @@ -897,22 +877,25 @@ public class WindowManagerService extends IWindowManager.Stub Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale); mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(), Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale); - mAnimatorDurationScale = Settings.System.getFloat(context.getContentResolver(), - Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale); + setAnimatorDurationScale(Settings.System.getFloat(context.getContentResolver(), + Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale)); // Track changes to DevicePolicyManager state so we can enable/disable keyguard. IntentFilter filter = new IntentFilter(); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + // Track user switching. + filter.addAction(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(mBroadcastReceiver, filter); mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK - | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG"); + | PowerManager.ON_AFTER_RELEASE, TAG); mHoldingScreenWakeLock.setReferenceCounted(false); mInputManager = new InputManagerService(context, mInputMonitor); - mAnimator = new WindowAnimator(this, context, mPolicy); + mFxSession = new SurfaceSession(); + mAnimator = new WindowAnimator(this); - PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); + PolicyThread thr = new PolicyThread(mPolicy, this, context); thr.start(); synchronized (thr) { @@ -928,7 +911,6 @@ public class WindowManagerService extends IWindowManager.Stub // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); - mFxSession = new SurfaceSession(); Surface.openTransaction(); createWatermark(); @@ -955,34 +937,31 @@ public class WindowManagerService extends IWindowManager.Stub } private void placeWindowAfter(WindowState pos, WindowState window) { - final int i = mWindows.indexOf(pos); + final WindowList windows = pos.getWindowList(); + final int i = windows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " - + (i+1) + " of " + mWindows.size() + " (after " + pos + ")"); - mWindows.add(i+1, window); + + (i+1) + " of " + windows.size() + " (after " + pos + ")"); + windows.add(i+1, window); mWindowsChanged = true; } private void placeWindowBefore(WindowState pos, WindowState window) { - final int i = mWindows.indexOf(pos); + final WindowList windows = pos.getWindowList(); + final int i = windows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " - + i + " of " + mWindows.size() + " (before " + pos + ")"); - mWindows.add(i, window); + + i + " of " + windows.size() + " (before " + pos + ")"); + windows.add(i, window); mWindowsChanged = true; } //This method finds out the index of a window that has the same app token as //win. used for z ordering the windows in mWindows private int findIdxBasedOnAppTokens(WindowState win) { - //use a local variable to cache mWindows - ArrayList<WindowState> localmWindows = mWindows; - int jmax = localmWindows.size(); - if(jmax == 0) { - return -1; - } - for(int j = (jmax-1); j >= 0; j--) { - WindowState wentry = localmWindows.get(j); + WindowList windows = win.getWindowList(); + for(int j = windows.size() - 1; j >= 0; j--) { + WindowState wentry = windows.get(j); if(wentry.mAppToken == win.mAppToken) { return j; } @@ -993,9 +972,9 @@ public class WindowManagerService extends IWindowManager.Stub private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) { final IWindow client = win.mClient; final WindowToken token = win.mToken; - final ArrayList<WindowState> localmWindows = mWindows; - final int N = localmWindows.size(); + final WindowList windows = win.getWindowList(); + final int N = windows.size(); final WindowState attached = win.mAttachedWindow; int i; if (attached == null) { @@ -1027,7 +1006,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.v(TAG, "Adding window " + win + " at " + (newIdx+1) + " of " + N); } - localmWindows.add(newIdx+1, win); + windows.add(newIdx+1, win); mWindowsChanged = true; } } @@ -1100,7 +1079,7 @@ public class WindowManagerService extends IWindowManager.Stub // Just search for the start of this layer. final int myLayer = win.mBaseLayer; for (i=0; i<N; i++) { - WindowState w = localmWindows.get(i); + WindowState w = windows.get(i); if (w.mBaseLayer > myLayer) { break; } @@ -1109,7 +1088,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.v(TAG, "Adding window " + win + " at " + i + " of " + N); } - localmWindows.add(i, win); + windows.add(i, win); mWindowsChanged = true; } } @@ -1118,7 +1097,7 @@ public class WindowManagerService extends IWindowManager.Stub // Figure out where window should go, based on layer. final int myLayer = win.mBaseLayer; for (i=N-1; i>=0; i--) { - if (localmWindows.get(i).mBaseLayer <= myLayer) { + if (windows.get(i).mBaseLayer <= myLayer) { i++; break; } @@ -1127,9 +1106,10 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + win + " at " + i + " of " + N); - localmWindows.add(i, win); + windows.add(i, win); mWindowsChanged = true; } + if (addToToken) { if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); token.windows.add(tokenWindowsPos, win); @@ -1157,8 +1137,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); token.windows.add(i, win); } - placeWindowBefore( - wSublayer >= 0 ? attached : w, win); + placeWindowBefore(wSublayer >= 0 ? attached : w, win); break; } } else { @@ -1226,13 +1205,16 @@ public class WindowManagerService extends IWindowManager.Stub * @return The index+1 in mWindows of the discovered target. */ int findDesiredInputMethodWindowIndexLocked(boolean willMove) { - final ArrayList<WindowState> localmWindows = mWindows; - final int N = localmWindows.size(); + // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the + // same display. Or even when the current IME/target are not on the same screen as the next + // IME/target. For now only look for input windows on the main screen. + WindowList windows = getDefaultWindowList(); + final int N = windows.size(); WindowState w = null; int i = N; while (i > 0) { i--; - w = localmWindows.get(i); + w = windows.get(i); if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG, "Checking window @" + i + " " + w + " fl=0x" + Integer.toHexString(w.mAttrs.flags)); @@ -1247,7 +1229,7 @@ public class WindowManagerService extends IWindowManager.Stub if (!willMove && w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING && i > 0) { - WindowState wb = localmWindows.get(i-1); + WindowState wb = windows.get(i-1); if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) { i--; w = wb; @@ -1267,12 +1249,13 @@ public class WindowManagerService extends IWindowManager.Stub // the IME shown: when the Dialog is dismissed, we want to keep // the IME above it until it is completely gone so it doesn't drop // behind the dialog or its full-screen scrim. - if (mInputMethodTarget != null && w != null - && mInputMethodTarget.isDisplayedLw() - && mInputMethodTarget.mExiting) { - if (mInputMethodTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) { - w = mInputMethodTarget; - i = localmWindows.indexOf(w); + 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); } } @@ -1281,20 +1264,20 @@ public class WindowManagerService extends IWindowManager.Stub + w + " willMove=" + willMove); if (willMove && w != null) { - final WindowState curTarget = mInputMethodTarget; - if (curTarget != null && curTarget.mAppToken != null) { + AppWindowToken token = curTarget == null ? null : curTarget.mAppToken; + if (token != null) { // Now some fun for dealing with window animations that // modify the Z order. We need to look at all windows below // the current target that are in this app, finding the highest // visible one in layering. - AppWindowToken token = curTarget.mAppToken; WindowState highestTarget = null; int highestPos = 0; if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) { - int pos = localmWindows.indexOf(curTarget); + WindowList curWindows = curTarget.getWindowList(); + int pos = curWindows.indexOf(curTarget); while (pos >= 0) { - WindowState win = localmWindows.get(pos); + WindowState win = curWindows.get(pos); if (win.mAppToken != token) { break; } @@ -1339,15 +1322,8 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Placing input method @" + (i+1)); if (w != null) { if (willMove) { - if (DEBUG_INPUT_METHOD) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - Slog.w(TAG, "Moving IM target from " - + mInputMethodTarget + " to " + w, e); - } + if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to " + + w + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4))); mInputMethodTarget = w; mInputMethodTargetWaitingAnim = false; if (w.mAppToken != null) { @@ -1359,15 +1335,8 @@ public class WindowManagerService extends IWindowManager.Stub return i+1; } if (willMove) { - if (DEBUG_INPUT_METHOD) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - Slog.w(TAG, "Moving IM target from " - + mInputMethodTarget + " to null", e); - } + if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to null." + + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4))); mInputMethodTarget = null; setInputMethodAnimLayerAdjustment(0); } @@ -1380,7 +1349,8 @@ public class WindowManagerService extends IWindowManager.Stub win.mTargetAppToken = mInputMethodTarget.mAppToken; if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding input method window " + win + " at " + pos); - mWindows.add(pos, win); + // TODO(multidisplay): IMEs are only supported on the default display. + getDefaultWindowList().add(pos, win); mWindowsChanged = true; moveInputMethodDialogsLocked(pos+1); return; @@ -1418,22 +1388,23 @@ public class WindowManagerService extends IWindowManager.Stub } private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { - int wpos = mWindows.indexOf(win); + WindowList windows = win.getWindowList(); + int wpos = windows.indexOf(win); if (wpos >= 0) { if (wpos < interestingPos) interestingPos--; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win); - mWindows.remove(wpos); + windows.remove(wpos); mWindowsChanged = true; int NC = win.mChildWindows.size(); while (NC > 0) { NC--; WindowState cw = win.mChildWindows.get(NC); - int cpos = mWindows.indexOf(cw); + int cpos = windows.indexOf(cw); if (cpos >= 0) { if (cpos < interestingPos) interestingPos--; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing child at " + cpos + ": " + cw); - mWindows.remove(cpos); + windows.remove(cpos); } } } @@ -1445,27 +1416,29 @@ public class WindowManagerService extends IWindowManager.Stub // This is a hack to get all of the child windows added as well // at the right position. Child windows should be rare and // this case should be rare, so it shouldn't be that big a deal. - int wpos = mWindows.indexOf(win); + WindowList windows = win.getWindowList(); + int wpos = windows.indexOf(win); if (wpos >= 0) { - if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos - + ": " + win); - mWindows.remove(wpos); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos + ": " + win); + windows.remove(wpos); mWindowsChanged = true; reAddWindowLocked(wpos, win); } } - void logWindowList(String prefix) { - int N = mWindows.size(); + void logWindowList(final WindowList windows, String prefix) { + int N = windows.size(); while (N > 0) { N--; - Slog.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); + Slog.v(TAG, prefix + "#" + N + ": " + windows.get(N)); } } void moveInputMethodDialogsLocked(int pos) { ArrayList<WindowState> dialogs = mInputMethodDialogs; + // TODO(multidisplay): IMEs are only supported on the default display. + WindowList windows = getDefaultWindowList(); final int N = dialogs.size(); if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i<N; i++) { @@ -1473,13 +1446,13 @@ public class WindowManagerService extends IWindowManager.Stub } if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "Window list w/pos=" + pos); - logWindowList(" "); + logWindowList(windows, " "); } if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; - if (pos < mWindows.size()) { - WindowState wp = mWindows.get(pos); + if (pos < windows.size()) { + WindowState wp = windows.get(pos); if (wp == mInputMethodWindow) { pos++; } @@ -1492,7 +1465,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "Final window list:"); - logWindowList(" "); + logWindowList(windows, " "); } return; } @@ -1502,7 +1475,7 @@ public class WindowManagerService extends IWindowManager.Stub reAddWindowToListInOrderLocked(win); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "No IM target, final list:"); - logWindowList(" "); + logWindowList(windows, " "); } } } @@ -1514,6 +1487,9 @@ public class WindowManagerService extends IWindowManager.Stub return false; } + // TODO(multidisplay): IMEs are only supported on the default display. + WindowList windows = getDefaultWindowList(); + int imPos = findDesiredInputMethodWindowIndexLocked(true); if (imPos >= 0) { // In this case, the input method windows are to be placed @@ -1521,9 +1497,9 @@ public class WindowManagerService extends IWindowManager.Stub // First check to see if the input method windows are already // located here, and contiguous. - final int N = mWindows.size(); + final int N = windows.size(); WindowState firstImWin = imPos < N - ? mWindows.get(imPos) : null; + ? windows.get(imPos) : null; // Figure out the actual input method window that should be // at the bottom of their stack. @@ -1539,7 +1515,7 @@ public class WindowManagerService extends IWindowManager.Stub // First find the top IM window. int pos = imPos+1; while (pos < N) { - if (!(mWindows.get(pos)).mIsImWindow) { + if (!(windows.get(pos)).mIsImWindow) { break; } pos++; @@ -1547,7 +1523,7 @@ public class WindowManagerService extends IWindowManager.Stub pos++; // Now there should be no more input method windows above. while (pos < N) { - if ((mWindows.get(pos)).mIsImWindow) { + if ((windows.get(pos)).mIsImWindow) { break; } pos++; @@ -1561,18 +1537,18 @@ public class WindowManagerService extends IWindowManager.Stub if (imWin != null) { if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "Moving IM from " + imPos); - logWindowList(" "); + logWindowList(windows, " "); } imPos = tmpRemoveWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "List after removing with new pos " + imPos + ":"); - logWindowList(" "); + logWindowList(windows, " "); } imWin.mTargetAppToken = mInputMethodTarget.mAppToken; reAddWindowLocked(imPos, imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "List after moving IM to " + imPos + ":"); - logWindowList(" "); + logWindowList(windows, " "); } if (DN > 0) moveInputMethodDialogsLocked(imPos+1); } else { @@ -1590,17 +1566,17 @@ public class WindowManagerService extends IWindowManager.Stub reAddWindowToListInOrderLocked(imWin); if (DEBUG_INPUT_METHOD) { Slog.v(TAG, "List with no IM target:"); - logWindowList(" "); + logWindowList(windows, " "); } - if (DN > 0) moveInputMethodDialogsLocked(-1);; + if (DN > 0) moveInputMethodDialogsLocked(-1); } else { - moveInputMethodDialogsLocked(-1);; + moveInputMethodDialogsLocked(-1); } } if (needAssignLayers) { - assignLayersLocked(); + assignLayersLocked(windows); } return true; @@ -1631,13 +1607,15 @@ public class WindowManagerService extends IWindowManager.Stub mInnerFields.mWallpaperMayChange = false; int changed = 0; - final int dw = mAppDisplayWidth; - final int dh = mAppDisplayHeight; + // TODO(multidisplay): Wallpapers on main screen only. + final DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo(); + final int dw = displayInfo.appWidth; + final int dh = displayInfo.appHeight; // First find top-most window that has asked to be on top of the // wallpaper; all wallpapers go behind it. - final ArrayList<WindowState> localmWindows = mWindows; - int N = localmWindows.size(); + final WindowList windows = getDefaultWindowList(); + int N = windows.size(); WindowState w = null; WindowState foundW = null; int foundI = 0; @@ -1647,7 +1625,7 @@ public class WindowManagerService extends IWindowManager.Stub int i = N; while (i > 0) { i--; - w = localmWindows.get(i); + w = windows.get(i); if ((w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER)) { if (topCurW == null) { topCurW = w; @@ -1656,7 +1634,7 @@ public class WindowManagerService extends IWindowManager.Stub continue; } topCurW = null; - if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) { + if (w != mWindowDetachedWallpaper && w.mAppToken != null) { // If this window's app token is hidden and not animating, // it is of no interest to us. if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) { @@ -1682,7 +1660,7 @@ public class WindowManagerService extends IWindowManager.Stub continue; } break; - } else if (w == mAnimator.mWindowDetachedWallpaper) { + } else if (w == mWindowDetachedWallpaper) { windowDetachedI = i; } } @@ -1741,7 +1719,7 @@ public class WindowManagerService extends IWindowManager.Stub + " old animation: " + oldAnim); } if (foundAnim && oldAnim) { - int oldI = localmWindows.indexOf(oldW); + int oldI = windows.indexOf(oldW); if (DEBUG_WALLPAPER) { Slog.v(TAG, "New i: " + foundI + " old i: " + oldI); } @@ -1760,7 +1738,7 @@ public class WindowManagerService extends IWindowManager.Stub mWallpaperTarget = oldW; foundW = oldW; foundI = oldI; - } + } // Now set the upper and lower wallpaper targets // correctly, and make sure that we are positioning // the wallpaper below the lower. @@ -1825,7 +1803,7 @@ public class WindowManagerService extends IWindowManager.Stub // AND any starting window associated with it, AND below the // maximum layer the policy allows for wallpapers. while (foundI > 0) { - WindowState wb = localmWindows.get(foundI-1); + WindowState wb = windows.get(foundI-1); if (wb.mBaseLayer < maxLayer && wb.mAttachedWindow != foundW && (foundW.mAttachedWindow == null || @@ -1851,7 +1829,7 @@ public class WindowManagerService extends IWindowManager.Stub } else { // Okay i is the position immediately above the wallpaper. Look at // what is below it for later. - foundW = foundI > 0 ? localmWindows.get(foundI-1) : null; + foundW = foundI > 0 ? windows.get(foundI-1) : null; } if (visible) { @@ -1901,18 +1879,18 @@ public class WindowManagerService extends IWindowManager.Stub if (wallpaper == foundW) { foundI--; foundW = foundI > 0 - ? localmWindows.get(foundI-1) : null; + ? windows.get(foundI-1) : null; continue; } // The window didn't match... the current wallpaper window, // wherever it is, is in the wrong place, so make sure it is // not in the list. - int oldIndex = localmWindows.indexOf(wallpaper); + int oldIndex = windows.indexOf(wallpaper); if (oldIndex >= 0) { if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at " + oldIndex + ": " + wallpaper); - localmWindows.remove(oldIndex); + windows.remove(oldIndex); mWindowsChanged = true; if (oldIndex < foundI) { foundI--; @@ -1925,7 +1903,7 @@ public class WindowManagerService extends IWindowManager.Stub + " from " + oldIndex + " to " + foundI); } - localmWindows.add(foundI, wallpaper); + windows.add(foundI, wallpaper); mWindowsChanged = true; changed |= ADJUST_WALLPAPER_LAYERS_CHANGED; } @@ -2040,14 +2018,11 @@ public class WindowManagerService extends IWindowManager.Stub } } - // TODO(cmautner): Move to WindowAnimator. - void setWallpaperOffset(final WindowStateAnimator winAnimator, final int left, final int top) { - mH.sendMessage(mH.obtainMessage(H.SET_WALLPAPER_OFFSET, left, top, winAnimator)); - } - void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { - final int dw = mAppDisplayWidth; - final int dh = mAppDisplayHeight; + final DisplayContent displayContent = changingTarget.mDisplayContent; + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int dw = displayInfo.appWidth; + final int dh = displayInfo.appHeight; WindowState target = mWallpaperTarget; if (target != null) { @@ -2079,19 +2054,8 @@ public class WindowManagerService extends IWindowManager.Stub // TODO(cmautner): Don't move this from here, just lock the WindowAnimator. if (winAnimator.mSurfaceX != wallpaper.mShownFrame.left || winAnimator.mSurfaceY != wallpaper.mShownFrame.top) { - Surface.openTransaction(); - try { - if (SHOW_TRANSACTIONS) logSurface(wallpaper, - "POS " + wallpaper.mShownFrame.left - + ", " + wallpaper.mShownFrame.top, null); - setWallpaperOffset(winAnimator, (int) wallpaper.mShownFrame.left, + winAnimator.setWallpaperOffset((int) wallpaper.mShownFrame.left, (int) wallpaper.mShownFrame.top); - } catch (RuntimeException e) { - Slog.w(TAG, "Error positioning surface of " + wallpaper - + " pos=(" + wallpaper.mShownFrame.left - + "," + wallpaper.mShownFrame.top + ")", e); - } - Surface.closeTransaction(); } // We only want to be synchronous with one wallpaper. sync = false; @@ -2120,8 +2084,10 @@ public class WindowManagerService extends IWindowManager.Stub void updateWallpaperVisibilityLocked() { final boolean visible = isWallpaperVisible(mWallpaperTarget); - final int dw = mAppDisplayWidth; - final int dh = mAppDisplayHeight; + final DisplayContent displayContent = mWallpaperTarget.mDisplayContent; + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int dw = displayInfo.appWidth; + final int dh = displayInfo.appHeight; int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { @@ -2146,12 +2112,12 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + public int addWindow(Session session, IWindow client, int seq, - WindowManager.LayoutParams attrs, int viewVisibility, + WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { int res = mPolicy.checkAddPermission(attrs); - if (res != WindowManagerImpl.ADD_OKAY) { + if (res != WindowManagerGlobal.ADD_OKAY) { return res; } @@ -2161,13 +2127,13 @@ public class WindowManagerService extends IWindowManager.Stub long origId; synchronized(mWindowMap) { - if (mDisplay == null) { + if (mDefaultDisplay == null) { throw new IllegalStateException("Display has not been initialialized"); } if (mWindowMap.containsKey(client.asBinder())) { Slog.w(TAG, "Window " + client + " is already added"); - return WindowManagerImpl.ADD_DUPLICATE_ADD; + return WindowManagerGlobal.ADD_DUPLICATE_ADD; } if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { @@ -2175,13 +2141,13 @@ public class WindowManagerService extends IWindowManager.Stub if (attachedWindow == null) { Slog.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; + return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { Slog.w(TAG, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; + return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } } @@ -2192,22 +2158,22 @@ public class WindowManagerService extends IWindowManager.Stub && attrs.type <= LAST_APPLICATION_WINDOW) { Slog.w(TAG, "Attempted to add application window with unknown token " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_APP_TOKEN; + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_APP_TOKEN; + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_APP_TOKEN; + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with unknown token " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_APP_TOKEN; + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } token = new WindowToken(this, attrs.token, -1, false); addToken = true; @@ -2217,68 +2183,69 @@ public class WindowManagerService extends IWindowManager.Stub if (atoken == null) { Slog.w(TAG, "Attempted to add window with non-application token " + token + ". Aborting."); - return WindowManagerImpl.ADD_NOT_APP_TOKEN; + return WindowManagerGlobal.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { Slog.w(TAG, "Attempted to add window with exiting application token " + token + ". Aborting."); - return WindowManagerImpl.ADD_APP_EXITING; + return WindowManagerGlobal.ADD_APP_EXITING; } if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (localLOGV) Slog.v( TAG, "**** NO NEED TO START: " + attrs.getTitle()); - return WindowManagerImpl.ADD_STARTING_NOT_NEEDED; + return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; } } else if (attrs.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 WindowManagerImpl.ADD_BAD_APP_TOKEN; + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (attrs.type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_APP_TOKEN; + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (attrs.type == TYPE_DREAM) { if (token.windowType != TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with bad token " + attrs.token + ". Aborting."); - return WindowManagerImpl.ADD_BAD_APP_TOKEN; + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } + final DisplayContent displayContent = getDisplayContent(displayId); win = new WindowState(this, session, client, token, - attachedWindow, seq, attrs, viewVisibility); + attachedWindow, seq, attrs, viewVisibility, displayContent); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to // continue. Slog.w(TAG, "Adding window client " + client.asBinder() + " that is dead, aborting."); - return WindowManagerImpl.ADD_APP_EXITING; + return WindowManagerGlobal.ADD_APP_EXITING; } mPolicy.adjustWindowParamsLw(win.mAttrs); res = mPolicy.prepareAddWindowLw(win, attrs); - if (res != WindowManagerImpl.ADD_OKAY) { + if (res != WindowManagerGlobal.ADD_OKAY) { return res; } - + if (outInputChannel != null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel); - + mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); } // From now on, no exceptions or errors allowed! - res = WindowManagerImpl.ADD_OKAY; + res = WindowManagerGlobal.ADD_OKAY; origId = Binder.clearCallingIdentity(); @@ -2322,10 +2289,10 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.getContentInsetHintLw(attrs, outContentInsets); if (mInTouchMode) { - res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE; + res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; } if (win.mAppToken == null || !win.mAppToken.clientHidden) { - res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE; + res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; } mInputMonitor.setUpdateInputWindowsNeededLw(); @@ -2343,7 +2310,7 @@ public class WindowManagerService extends IWindowManager.Stub moveInputMethodWindowsIfNeededLocked(false); } - assignLayersLocked(); + assignLayersLocked(displayContent.getWindowList()); // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. @@ -2357,7 +2324,7 @@ public class WindowManagerService extends IWindowManager.Stub if (localLOGV) Slog.v( TAG, "New client " + client.asBinder() + ": window=" + win); - + if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) { reportNewConfig = true; } @@ -2485,7 +2452,9 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win); mWindowMap.remove(win.mClient.asBinder()); - mWindows.remove(win); + + final WindowList windows = win.getWindowList(); + windows.remove(win); mPendingRemove.remove(win); mWindowsChanged = true; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win); @@ -2543,14 +2512,14 @@ public class WindowManagerService extends IWindowManager.Stub } if (!mInLayout) { - assignLayersLocked(); + assignLayersLocked(windows); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } } - + mInputMonitor.updateInputWindowsLw(true /*force*/); } @@ -2683,6 +2652,32 @@ public class WindowManagerService extends IWindowManager.Stub return null; } + public void setUniverseTransformLocked(WindowState window, float alpha, + float offx, float offy, float dsdx, float dtdx, float dsdy, float dtdy) { + Transformation transform = window.mWinAnimator.mUniverseTransform; + transform.setAlpha(alpha); + Matrix matrix = transform.getMatrix(); + matrix.getValues(mTmpFloats); + mTmpFloats[Matrix.MTRANS_X] = offx; + mTmpFloats[Matrix.MTRANS_Y] = offy; + mTmpFloats[Matrix.MSCALE_X] = dsdx; + mTmpFloats[Matrix.MSKEW_Y] = dtdx; + mTmpFloats[Matrix.MSKEW_X] = dsdy; + mTmpFloats[Matrix.MSCALE_Y] = dtdy; + matrix.setValues(mTmpFloats); + final DisplayInfo displayInfo = window.mDisplayContent.getDisplayInfo(); + final RectF dispRect = new RectF(0, 0, + displayInfo.logicalWidth, displayInfo.logicalHeight); + matrix.mapRect(dispRect); + window.mGivenTouchableRegion.set(0, 0, + displayInfo.logicalWidth, displayInfo.logicalHeight); + window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top, + (int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE); + window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; + mLayoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + public int relayoutWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, @@ -2729,7 +2724,7 @@ public class WindowManagerService extends IWindowManager.Stub } winAnimator.mSurfaceDestroyDeferred = - (flags&WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY) != 0; + (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0; int attrChanges = 0; int flagChanges = 0; @@ -2848,7 +2843,7 @@ public class WindowManagerService extends IWindowManager.Stub } } catch (Exception e) { mInputMonitor.updateInputWindowsLw(true /*force*/); - + Slog.w(TAG, "Exception thrown when creating surface for client " + client + " (" + win.mAttrs.getTitle() + ")", e); @@ -2948,14 +2943,16 @@ public class WindowManagerService extends IWindowManager.Stub } mLayoutNeeded = true; - win.mGivenInsetsPending = (flags&WindowManagerImpl.RELAYOUT_INSETS_PENDING) != 0; + win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; if (assignLayers) { - assignLayersLocked(); + assignLayersLocked(win.getWindowList()); } configChanged = updateOrientationFromAppTokensLocked(false); performLayoutAndPlaceSurfacesLocked(); if (toBeDisplayed && win.mIsWallpaper) { - updateWallpaperOffsetLocked(win, mAppDisplayWidth, mAppDisplayHeight, false); + DisplayInfo displayInfo = getDefaultDisplayInfo(); + updateWallpaperOffsetLocked(win, + displayInfo.appWidth, displayInfo.appHeight, false); } if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); @@ -2989,10 +2986,10 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); - return (inTouchMode ? WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE : 0) - | (toBeDisplayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0) - | (surfaceChanged ? WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED : 0) - | (animating ? WindowManagerImpl.RELAYOUT_RES_ANIMATING : 0); + return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0) + | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0) + | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0) + | (animating ? WindowManagerGlobal.RELAYOUT_RES_ANIMATING : 0); } public void performDeferredDestroyWindow(Session session, IWindow client) { @@ -3042,7 +3039,12 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } + @Override public float getWindowCompatibilityScale(IBinder windowToken) { + if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, + "getWindowCompatibilityScale()")) { + throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); + } synchronized (mWindowMap) { WindowState windowState = mWindowMap.get(windowToken); return (windowState != null) ? windowState.mGlobalScale : 1.0f; @@ -3128,12 +3130,11 @@ public class WindowManagerService extends IWindowManager.Stub a.setDetachWallpaper(true); a.setDuration(duration); return a; - } else { - // For normal animations, the exiting element just holds in place. - Animation a = new AlphaAnimation(1, 1); - a.setDuration(duration); - return a; } + // For normal animations, the exiting element just holds in place. + Animation a = new AlphaAnimation(1, 1); + a.setDuration(duration); + return a; } /** @@ -3170,10 +3171,12 @@ public class WindowManagerService extends IWindowManager.Stub duration = 300; break; } + // TODO(multidisplay): For now assume all app animation is on main display. + final DisplayInfo displayInfo = getDefaultDisplayInfo(); if (enter) { // Entering app zooms out from the center of the initial rect. - float scaleW = mNextAppTransitionStartWidth / (float) mAppDisplayWidth; - float scaleH = mNextAppTransitionStartHeight / (float) mAppDisplayHeight; + float scaleW = mNextAppTransitionStartWidth / (float) displayInfo.appWidth; + float scaleH = mNextAppTransitionStartHeight / (float) displayInfo.appHeight; Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, scaleW), computePivot(mNextAppTransitionStartY, scaleH)); @@ -3193,13 +3196,13 @@ public class WindowManagerService extends IWindowManager.Stub final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.decelerate_cubic); a.setInterpolator(interpolator); - a.initialize(mAppDisplayWidth, mAppDisplayHeight, - mAppDisplayWidth, mAppDisplayHeight); + a.initialize(displayInfo.appWidth, displayInfo.appHeight, + displayInfo.appWidth, displayInfo.appHeight); return a; } private Animation createThumbnailAnimationLocked(int transit, - boolean enter, boolean thumb, boolean delayed) { + boolean enter, boolean thumb, boolean scaleUp) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; @@ -3209,7 +3212,6 @@ public class WindowManagerService extends IWindowManager.Stub // it is the standard duration for that. Otherwise we use the longer // task transition duration. int duration; - int delayDuration = delayed ? 270 : 0; switch (transit) { case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: @@ -3217,62 +3219,102 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.integer.config_shortAnimTime); break; default: - duration = delayed ? 250 : 300; + duration = 250; break; } + // TOOD(multidisplay): For now assume all app animation is on the main screen. + DisplayInfo displayInfo = getDefaultDisplayInfo(); if (thumb) { // Animation for zooming thumbnail from its initial size to // filling the screen. - float scaleW = mAppDisplayWidth/thumbWidth; - float scaleH = mAppDisplayHeight/thumbHeight; - - Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, - computePivot(mNextAppTransitionStartX, 1/scaleW), - computePivot(mNextAppTransitionStartY, 1/scaleH)); - AnimationSet set = new AnimationSet(true); - Animation alpha = new AlphaAnimation(1, 0); - scale.setDuration(duration); - scale.setInterpolator( - new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); - set.addAnimation(scale); - alpha.setDuration(duration); - set.addAnimation(alpha); - set.setFillBefore(true); - if (delayDuration > 0) { - set.setStartOffset(delayDuration); + if (scaleUp) { + float scaleW = displayInfo.appWidth / thumbWidth; + float scaleH = displayInfo.appHeight / thumbHeight; + + Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, + computePivot(mNextAppTransitionStartX, 1 / scaleW), + computePivot(mNextAppTransitionStartY, 1 / scaleH)); + AnimationSet set = new AnimationSet(true); + Animation alpha = new AlphaAnimation(1, 0); + scale.setDuration(duration); + scale.setInterpolator( + new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); + set.addAnimation(scale); + alpha.setDuration(duration); + set.addAnimation(alpha); + set.setFillBefore(true); + a = set; + } else { + float scaleW = displayInfo.appWidth / thumbWidth; + float scaleH = displayInfo.appHeight / thumbHeight; + + Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, + computePivot(mNextAppTransitionStartX, 1 / scaleW), + computePivot(mNextAppTransitionStartY, 1 / scaleH)); + AnimationSet set = new AnimationSet(true); + Animation alpha = new AlphaAnimation(1, 1); + scale.setDuration(duration); + scale.setInterpolator( + new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); + set.addAnimation(scale); + alpha.setDuration(duration); + set.addAnimation(alpha); + set.setFillBefore(true); + + a = set; } - a = set; } else if (enter) { // Entering app zooms out from the center of the thumbnail. - float scaleW = thumbWidth / mAppDisplayWidth; - float scaleH = thumbHeight / mAppDisplayHeight; - Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, - computePivot(mNextAppTransitionStartX, scaleW), - computePivot(mNextAppTransitionStartY, scaleH)); - scale.setDuration(duration); - scale.setInterpolator( - new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); - scale.setFillBefore(true); - if (delayDuration > 0) { - scale.setStartOffset(delayDuration); + if (scaleUp) { + float scaleW = thumbWidth / displayInfo.appWidth; + float scaleH = thumbHeight / displayInfo.appHeight; + Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, + computePivot(mNextAppTransitionStartX, scaleW), + computePivot(mNextAppTransitionStartY, scaleH)); + scale.setDuration(duration); + scale.setInterpolator( + new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); + scale.setFillBefore(true); + a = scale; + } else { + // noop animation + a = new AlphaAnimation(1, 1); + a.setDuration(duration); } - a = scale; } else { - if (delayed) { - a = new AlphaAnimation(1, 0); - a.setStartOffset(0); - a.setDuration(delayDuration - 120); - a.setBackgroundColor(0xFF000000); + // Exiting app + if (scaleUp) { + // noop animation + a = new AlphaAnimation(1, 1); + a.setDuration(duration); } else { - a = createExitAnimationLocked(transit, duration); + float scaleW = thumbWidth / displayInfo.appWidth; + float scaleH = thumbHeight / displayInfo.appHeight; + Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, + computePivot(mNextAppTransitionStartX, scaleW), + computePivot(mNextAppTransitionStartY, scaleH)); + scale.setDuration(duration); + scale.setInterpolator( + new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); + scale.setFillBefore(true); + AnimationSet set = new AnimationSet(true); + Animation alpha = new AlphaAnimation(1, 0); + set.addAnimation(scale); + alpha.setDuration(duration); + alpha.setInterpolator(new DecelerateInterpolator( + THUMBNAIL_ANIMATION_DECELERATE_FACTOR)); + set.addAnimation(alpha); + set.setFillBefore(true); + set.setZAdjustment(Animation.ZORDER_TOP); + a = set; } } a.setFillAfter(true); final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.decelerate_quad); a.setInterpolator(interpolator); - a.initialize(mAppDisplayWidth, mAppDisplayHeight, - mAppDisplayWidth, mAppDisplayHeight); + a.initialize(displayInfo.appWidth, displayInfo.appHeight, + displayInfo.appWidth, displayInfo.appHeight); return a; } @@ -3299,14 +3341,14 @@ public class WindowManagerService extends IWindowManager.Stub "applyAnimation: wtoken=" + wtoken + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP" + " transit=" + transit + " Callers " + Debug.getCallers(3)); - } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL || - mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED) { - boolean delayed = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED); - a = createThumbnailAnimationLocked(transit, enter, false, delayed); + } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP || + mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN) { + boolean scaleUp = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP); + a = createThumbnailAnimationLocked(transit, enter, false, scaleUp); initialized = true; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { - String animName = delayed ? "ANIM_THUMBNAIL_DELAYED" : "ANIM_THUMBNAIL"; + String animName = scaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; Slog.v(TAG, "applyAnimation: wtoken=" + wtoken + " anim=" + a + " nextAppTransition=" + animName + " transit=" + transit + " Callers " + Debug.getCallers(3)); @@ -3440,7 +3482,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, msg); return false; } - + boolean okToDisplay() { return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOnFully(); } @@ -3470,10 +3512,12 @@ public class WindowManagerService extends IWindowManager.Stub mTokenMap.put(token, wtoken); if (type == TYPE_WALLPAPER) { mWallpaperTokens.add(wtoken); + updateLayoutToAnimWallpaperTokens(); } } } + @Override public void removeWindowToken(IBinder token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "removeWindowToken()")) { @@ -3499,7 +3543,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (win.isVisibleNow()) { - win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false); + win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, + false); changed = true; } } @@ -3515,6 +3560,7 @@ public class WindowManagerService extends IWindowManager.Stub mExitingTokens.add(wtoken); } else if (wtoken.windowType == TYPE_WALLPAPER) { mWallpaperTokens.remove(wtoken); + updateLayoutToAnimWallpaperTokens(); } } @@ -3591,6 +3637,7 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override public void setAppGroupId(IBinder token, int groupId) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppGroupId()")) { @@ -3617,9 +3664,11 @@ public class WindowManagerService extends IWindowManager.Stub return mLastWindowForcedOrientation; } - int pos = mWindows.size() - 1; + // TODO(multidisplay): Change to the correct display. + final WindowList windows = getDefaultWindowList(); + int pos = windows.size() - 1; while (pos >= 0) { - WindowState wtoken = mWindows.get(pos); + WindowState wtoken = windows.get(pos); pos--; if (wtoken.mAppToken != null) { // We hit an application window. so the orientation will be determined by the @@ -3633,9 +3682,9 @@ public class WindowManagerService extends IWindowManager.Stub if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) || (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ continue; - } else { - return (mLastWindowForcedOrientation=req); } + + return (mLastWindowForcedOrientation=req); } return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } @@ -3685,7 +3734,7 @@ public class WindowManagerService extends IWindowManager.Stub haveGroup = true; curGroup = wtoken.groupId; lastOrientation = wtoken.requestedOrientation; - } + } int or = wtoken.requestedOrientation; // If this application is fullscreen, and didn't explicitly say @@ -3712,6 +3761,7 @@ public class WindowManagerService extends IWindowManager.Stub return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } + @Override public Configuration updateOrientationFromAppTokens( Configuration currentConfig, IBinder freezeThisOneIfNeeded) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, @@ -3721,7 +3771,7 @@ public class WindowManagerService extends IWindowManager.Stub Configuration config = null; long ident = Binder.clearCallingIdentity(); - + synchronized(mWindowMap) { config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded); @@ -3762,7 +3812,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } - + return config; } @@ -3773,7 +3823,7 @@ public class WindowManagerService extends IWindowManager.Stub * setNewConfiguration() TO TELL THE WINDOW MANAGER IT CAN UNFREEZE THE * SCREEN. This will typically be done for you if you call * sendNewConfiguration(). - * + * * The orientation is computed from non-application windows first. If none of * the non-application windows specify orientation, the orientation is computed from * application tokens. @@ -3810,6 +3860,7 @@ public class WindowManagerService extends IWindowManager.Stub return req; } + @Override public void setNewConfiguration(Configuration config) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setNewConfiguration()")) { @@ -3822,7 +3873,8 @@ public class WindowManagerService extends IWindowManager.Stub performLayoutAndPlaceSurfacesLocked(); } } - + + @Override public void setAppOrientation(IApplicationToken token, int requestedOrientation) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppOrientation()")) { @@ -3971,14 +4023,14 @@ public class WindowManagerService extends IWindowManager.Stub } public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, - int startY, IRemoteCallback startedCallback, boolean delayed) { + int startY, IRemoteCallback startedCallback, boolean scaleUp) { synchronized(mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { - mNextAppTransitionType = delayed - ? ActivityOptions.ANIM_THUMBNAIL_DELAYED : ActivityOptions.ANIM_THUMBNAIL; + mNextAppTransitionType = scaleUp + ? ActivityOptions.ANIM_THUMBNAIL_SCALE_UP : ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; mNextAppTransitionPackage = null; mNextAppTransitionThumbnail = srcThumb; - mNextAppTransitionDelayed = delayed; + mNextAppTransitionScaleUp = scaleUp; mNextAppTransitionStartX = startX; mNextAppTransitionStartY = startY; scheduleAnimationCallback(mNextAppTransitionCallback); @@ -4075,7 +4127,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) { Slog.v(TAG, "Removing starting window: " + startingWindow); } - mWindows.remove(startingWindow); + startingWindow.getWindowList().remove(startingWindow); mWindowsChanged = true; if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing starting " + startingWindow + " from " + ttoken); @@ -4612,14 +4664,14 @@ public class WindowManagerService extends IWindowManager.Stub for (int i=0; i<NW; i++) { WindowState win = token.windows.get(i); if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing app window " + win); - mWindows.remove(win); + win.getWindowList().remove(win); int j = win.mChildWindows.size(); while (j > 0) { j--; WindowState cwin = win.mChildWindows.get(j); if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing child window " + cwin); - mWindows.remove(cwin); + cwin.getWindowList().remove(cwin); } } return NW > 0; @@ -4638,19 +4690,22 @@ public class WindowManagerService extends IWindowManager.Stub } void dumpWindowsLocked() { - for (int i=mWindows.size()-1; i>=0; i--) { - Slog.v(TAG, " #" + i + ": " + mWindows.get(i)); + int i = 0; + final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); + while (iterator.hasNext()) { + final WindowState w = iterator.next(); + Slog.v(TAG, " #" + i++ + ": " + w); } } - private int findWindowOffsetLocked(int tokenPos) { - final int NW = mWindows.size(); + private int findWindowOffsetLocked(WindowList windows, int tokenPos) { + final int NW = windows.size(); if (tokenPos >= mAnimatingAppTokens.size()) { int i = NW; while (i > 0) { i--; - WindowState win = mWindows.get(i); + WindowState win = windows.get(i); if (win.getAppToken() != null) { return i+1; } @@ -4660,7 +4715,7 @@ public class WindowManagerService extends IWindowManager.Stub while (tokenPos > 0) { // Find the first app token below the new position that has // a window displayed. - final AppWindowToken wtoken = mAnimatingAppTokens.get(tokenPos-1); + final AppWindowToken wtoken = mAppTokens.get(tokenPos-1); if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows @ " + tokenPos + " -- " + wtoken.token); if (wtoken.sendingToBottom) { @@ -4679,7 +4734,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowState cwin = win.mChildWindows.get(j); if (cwin.mSubLayer >= 0) { for (int pos=NW-1; pos>=0; pos--) { - if (mWindows.get(pos) == cwin) { + if (windows.get(pos) == cwin) { if (DEBUG_REORDER) Slog.v(TAG, "Found child win @" + (pos+1)); return pos+1; @@ -4688,7 +4743,7 @@ public class WindowManagerService extends IWindowManager.Stub } } for (int pos=NW-1; pos>=0; pos--) { - if (mWindows.get(pos) == win) { + if (windows.get(pos) == win) { if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos+1)); return pos+1; } @@ -4701,6 +4756,7 @@ public class WindowManagerService extends IWindowManager.Stub } private final int reAddWindowLocked(int index, WindowState win) { + final WindowList windows = win.getWindowList(); final int NCW = win.mChildWindows.size(); boolean added = false; for (int j=0; j<NCW; j++) { @@ -4709,31 +4765,35 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at " + index + ": " + cwin); win.mRebuilding = false; - mWindows.add(index, win); + windows.add(index, win); index++; added = true; } if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at " + index + ": " + cwin); cwin.mRebuilding = false; - mWindows.add(index, cwin); + windows.add(index, cwin); index++; } if (!added) { if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at " + index + ": " + win); win.mRebuilding = false; - mWindows.add(index, win); + windows.add(index, win); index++; } mWindowsChanged = true; return index; } - private final int reAddAppWindowsLocked(int index, WindowToken token) { + private final int reAddAppWindowsLocked(final DisplayContent displayContent, int index, + WindowToken token) { final int NW = token.windows.size(); for (int i=0; i<NW; i++) { - index = reAddWindowLocked(index, token.windows.get(i)); + final WindowState win = token.windows.get(i); + if (win.mDisplayContent == displayContent) { + index = reAddWindowLocked(index, token.windows.get(i)); + } } return index; } @@ -4779,7 +4839,13 @@ public class WindowManagerService extends IWindowManager.Stub if (tmpRemoveAppWindowsLocked(wtoken)) { if (DEBUG_REORDER) Slog.v(TAG, "Adding windows back in:"); if (DEBUG_REORDER) dumpWindowsLocked(); - reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while(iterator.hasNext()) { + final DisplayContent displayContent = iterator.next(); + final WindowList windows = displayContent.getWindowList(); + final int pos = findWindowOffsetLocked(windows, index); + reAddAppWindowsLocked(displayContent, pos, wtoken); + } if (DEBUG_REORDER) Slog.v(TAG, "Final window list:"); if (DEBUG_REORDER) dumpWindowsLocked(); updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, @@ -4818,18 +4884,25 @@ public class WindowManagerService extends IWindowManager.Stub // First remove all of the windows from the list. tmpRemoveAppWindowsLocked(wtoken); - // Where to start adding? - int pos = findWindowOffsetLocked(tokenPos); - // And now add them back at the correct place. - pos = reAddAppWindowsLocked(pos, wtoken); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + final DisplayContent displayContent = iterator.next(); + final WindowList windows = displayContent.getWindowList(); + final int pos = findWindowOffsetLocked(windows, tokenPos); + reAddAppWindowsLocked(displayContent, pos, wtoken); + + if (updateFocusAndLayout && !updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/)) { + assignLayersLocked(windows); + } + } if (updateFocusAndLayout) { mInputMonitor.setUpdateInputWindowsNeededLw(); - if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, - false /*updateInputWindows*/)) { - assignLayersLocked(); - } + + // Note that the above updateFocusedWindowLocked conditional used to sit here. + mLayoutNeeded = true; if (!mInLayout) { performLayoutAndPlaceSurfacesLocked(); @@ -4849,22 +4922,29 @@ public class WindowManagerService extends IWindowManager.Stub } } - // Where to start adding? - int pos = findWindowOffsetLocked(tokenPos); - // And now add them back at the correct place. - for (i=0; i<N; i++) { - WindowToken token = mTokenMap.get(tokens.get(i)); - if (token != null) { - pos = reAddAppWindowsLocked(pos, token); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + final DisplayContent displayContent = iterator.next(); + final WindowList windows = displayContent.getWindowList(); + // Where to start adding? + int pos = findWindowOffsetLocked(windows, tokenPos); + for (i=0; i<N; i++) { + WindowToken token = mTokenMap.get(tokens.get(i)); + if (token != null) { + pos = reAddAppWindowsLocked(displayContent, pos, token); + } + } + if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/)) { + assignLayersLocked(windows); } } mInputMonitor.setUpdateInputWindowsNeededLw(); - if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, - false /*updateInputWindows*/)) { - assignLayersLocked(); - } + + // Note that the above updateFocusedWindowLocked used to sit here. + mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); mInputMonitor.updateInputWindowsLw(false /*force*/); @@ -4946,59 +5026,26 @@ public class WindowManagerService extends IWindowManager.Stub // Misc IWindowSession methods // ------------------------------------------------------------- - private boolean shouldAllowDisableKeyguard() - { - // We fail safe and prevent disabling keyguard in the unlikely event this gets - // called before DevicePolicyManagerService has started. - if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) { - DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( - Context.DEVICE_POLICY_SERVICE); - if (dpm != null) { - mAllowDisableKeyguard = dpm.getPasswordQuality(null) - == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ? - ALLOW_DISABLE_YES : ALLOW_DISABLE_NO; - } - } - return mAllowDisableKeyguard == ALLOW_DISABLE_YES; - } - + @Override public void disableKeyguard(IBinder token, String tag) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } - synchronized (mKeyguardTokenWatcher) { - mKeyguardTokenWatcher.acquire(token, tag); - } + mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( + KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag))); } + @Override public void reenableKeyguard(IBinder token) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } - synchronized (mKeyguardTokenWatcher) { - mKeyguardTokenWatcher.release(token); - - if (!mKeyguardTokenWatcher.isAcquired()) { - // If we are the last one to reenable the keyguard wait until - // we have actually finished reenabling until returning. - // It is possible that reenableKeyguard() can be called before - // the previous disableKeyguard() is handled, in which case - // neither mKeyguardTokenWatcher.acquired() or released() would - // be called. In that case mKeyguardDisabled will be false here - // and we have nothing to wait for. - while (mKeyguardDisabled) { - try { - mKeyguardTokenWatcher.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - } + mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( + KeyguardDisableHandler.KEYGUARD_REENABLE, token)); } /** @@ -5044,8 +5091,9 @@ public class WindowManagerService extends IWindowManager.Stub public void closeSystemDialogs(String reason) { synchronized(mWindowMap) { - for (int i=mWindows.size()-1; i>=0; i--) { - WindowState w = mWindows.get(i); + final AllWindowsIterator iterator = new AllWindowsIterator(); + while (iterator.hasNext()) { + final WindowState w = iterator.next(); if (w.mHasSurface) { try { w.mClient.closeSystemDialogs(reason); @@ -5095,7 +5143,7 @@ public class WindowManagerService extends IWindowManager.Stub mTransitionAnimationScale = fixScale(scales[1]); } if (scales.length >= 3) { - mAnimatorDurationScale = fixScale(scales[2]); + setAnimatorDurationScale(fixScale(scales[2])); } } @@ -5103,6 +5151,11 @@ public class WindowManagerService extends IWindowManager.Stub mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget(); } + private void setAnimatorDurationScale(float scale) { + mAnimatorDurationScale = scale; + ValueAnimator.setDurationScale(scale); + } + public float getAnimationScale(int which) { switch (which) { case 0: return mWindowAnimationScale; @@ -5148,17 +5201,20 @@ public class WindowManagerService extends IWindowManager.Stub // Called by window manager policy. Not exposed externally. @Override - public void shutdown() { - ShutdownThread.shutdown(mContext, true); + public void shutdown(boolean confirm) { + ShutdownThread.shutdown(mContext, confirm); } // Called by window manager policy. Not exposed externally. @Override - public void rebootSafeMode() { - ShutdownThread.rebootSafeMode(mContext, true); + public void rebootSafeMode(boolean confirm) { + ShutdownThread.rebootSafeMode(mContext, confirm); } - public void setInputFilter(InputFilter filter) { + public void setInputFilter(IInputFilter filter) { + if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) { + throw new SecurityException("Requires FILTER_EVENTS permission"); + } mInputManager.setInputFilter(filter); } @@ -5247,9 +5303,11 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_enableWallpaperService) && !mOnlyCore; boolean haveKeyguard = true; - final int N = mWindows.size(); + // TODO(multidisplay): Expand to all displays? + final WindowList windows = getDefaultWindowList(); + final int N = windows.size(); for (int i=0; i<N; i++) { - WindowState w = mWindows.get(i); + WindowState w = windows.get(i); if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD) { // Only if there is a keyguard attached to the window manager // will we consider ourselves as having a keyguard. If it @@ -5401,8 +5459,9 @@ public class WindowManagerService extends IWindowManager.Stub // the background..) if (on) { boolean isVisible = false; - for (int i = mWindows.size() - 1; i >= 0; i--) { - final WindowState ws = mWindows.get(i); + final AllWindowsIterator iterator = new AllWindowsIterator(); + while (iterator.hasNext()) { + final WindowState ws = iterator.next(); if (ws.mSession.mPid == pid && ws.isVisibleLw()) { isVisible = true; break; @@ -5418,7 +5477,7 @@ public class WindowManagerService extends IWindowManager.Stub Surface.openTransaction(); try { if (mStrictModeFlash == null) { - mStrictModeFlash = new StrictModeFlash(mDisplay, mFxSession); + mStrictModeFlash = new StrictModeFlash(mDefaultDisplay, mFxSession); } mStrictModeFlash.setVisibility(on); } finally { @@ -5438,10 +5497,11 @@ public class WindowManagerService extends IWindowManager.Stub * In portrait mode, it grabs the upper region of the screen based on the vertical dimension * of the target image. * + * @param displayId the Display to take a screenshot of. * @param width the width of the target bitmap * @param height the height of the target bitmap */ - public Bitmap screenshotApplications(IBinder appToken, int width, int height) { + public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height) { if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "screenshotApplications()")) { throw new SecurityException("Requires READ_FRAME_BUFFER permission"); @@ -5459,8 +5519,10 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { long ident = Binder.clearCallingIdentity(); - dw = mCurDisplayWidth; - dh = mCurDisplayHeight; + final DisplayContent displayContent = getDisplayContent(displayId); + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + dw = displayInfo.logicalWidth; + dh = displayInfo.logicalHeight; int aboveAppLayer = mPolicy.windowTypeToLayerLw( WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER @@ -5474,8 +5536,9 @@ public class WindowManagerService extends IWindowManager.Stub // Figure out the part of the screen that is actually the app. boolean including = false; - for (int i=mWindows.size()-1; i>=0; i--) { - WindowState ws = mWindows.get(i); + final WindowList windows = displayContent.getWindowList(); + for (int i = windows.size() - 1; i >= 0; i--) { + WindowState ws = windows.get(i); if (!ws.mHasSurface) { continue; } @@ -5527,7 +5590,7 @@ public class WindowManagerService extends IWindowManager.Stub } // The screenshot API does not apply the current screen rotation. - rot = mDisplay.getRotation(); + rot = mDefaultDisplay.getRotation(); int fw = frame.width(); int fh = frame.height(); @@ -5562,10 +5625,11 @@ public class WindowManagerService extends IWindowManager.Stub } if (DEBUG_SCREENSHOT) { Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer); - for (int i=0; i<mWindows.size(); i++) { - Slog.i(TAG, mWindows.get(i) + ": " + mWindows.get(i).mLayer - + " animLayer=" + mWindows.get(i).mWinAnimator.mAnimLayer - + " surfaceLayer=" + mWindows.get(i).mWinAnimator.mSurfaceLayer); + for (int i = 0; i < windows.size(); i++) { + WindowState win = windows.get(i); + Slog.i(TAG, win + ": " + win.mLayer + + " animLayer=" + win.mWinAnimator.mAnimLayer + + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer); } } rawss = Surface.screenshot(dw, dh, 0, maxLayer); @@ -5687,6 +5751,7 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } + // TODO(multidisplay): Rotate any display? /** * Updates the current rotation. * @@ -5754,8 +5819,7 @@ public class WindowManagerService extends IWindowManager.Stub mWaitingForConfig = true; mLayoutNeeded = true; startFreezingDisplayLocked(inTransaction); - mInputManager.setDisplayOrientation(0, rotation, - mDisplay != null ? mDisplay.getExternalRotation() : Surface.ROTATION_0); + mInputManager.setDisplayOrientation(0, rotation, Surface.ROTATION_0); // We need to update our screen size information to match the new // rotation. Note that this is redundant with the later call to @@ -5770,6 +5834,8 @@ public class WindowManagerService extends IWindowManager.Stub ">>> OPEN TRANSACTION setRotationUnchecked"); Surface.openTransaction(); } + final DisplayContent displayContent = getDefaultDisplayContent(); + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); try { // NOTE: We disable the rotation in the emulator because // it doesn't support hardware OpenGL emulation yet. @@ -5777,8 +5843,8 @@ public class WindowManagerService extends IWindowManager.Stub && mAnimator.mScreenRotationAnimation.hasScreenshot()) { if (mAnimator.mScreenRotationAnimation.setRotation(rotation, mFxSession, MAX_ANIMATION_DURATION, mTransitionAnimationScale, - mCurDisplayWidth, mCurDisplayHeight)) { - scheduleAnimationLocked(); + displayInfo.logicalWidth, displayInfo.logicalHeight)) { + updateLayoutToAnimationLocked(); } } Surface.setOrientation(0, rotation); @@ -5790,10 +5856,11 @@ public class WindowManagerService extends IWindowManager.Stub } } - rebuildBlackFrame(); + rebuildBlackFrameLocked(); - for (int i=mWindows.size()-1; i>=0; i--) { - WindowState w = mWindows.get(i); + final WindowList windows = displayContent.getWindowList(); + for (int i = windows.size() - 1; i >= 0; i--) { + WindowState w = windows.get(i); if (w.mHasSurface) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w); w.mOrientationChanging = true; @@ -5864,7 +5931,9 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { final int rotation = getRotation(); - if (mInitialDisplayWidth < mInitialDisplayHeight) { + // TODO(multidisplay): Assume that such devices physical keys are on the main screen. + final DisplayContent displayContent = getDefaultDisplayContent(); + if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) { // On devices with a natural orientation of portrait switch (rotation) { default: @@ -5875,7 +5944,7 @@ public class WindowManagerService extends IWindowManager.Stub case Surface.ROTATION_180: return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; case Surface.ROTATION_270: - return Gravity.LEFT | Gravity.BOTTOM; + return Gravity.START | Gravity.BOTTOM; } } else { // On devices with a natural orientation of landscape @@ -5886,7 +5955,7 @@ public class WindowManagerService extends IWindowManager.Stub case Surface.ROTATION_90: return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; case Surface.ROTATION_180: - return Gravity.LEFT | Gravity.BOTTOM; + return Gravity.START | Gravity.BOTTOM; case Surface.ROTATION_270: return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; } @@ -6000,10 +6069,13 @@ public class WindowManagerService extends IWindowManager.Stub boolean result = true; - WindowState[] windows; + WindowList windows = new WindowList(); synchronized (mWindowMap) { //noinspection unchecked - windows = mWindows.toArray(new WindowState[mWindows.size()]); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while(iterator.hasNext()) { + windows.addAll(iterator.next().getWindowList()); + } } BufferedWriter out = null; @@ -6013,9 +6085,9 @@ public class WindowManagerService extends IWindowManager.Stub OutputStream clientStream = client.getOutputStream(); out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); - final int count = windows.length; + final int count = windows.size(); for (int i = 0; i < count; i++) { - final WindowState w = windows[i]; + final WindowState w = windows.get(i); out.write(Integer.toHexString(System.identityHashCode(w))); out.write(' '); out.append(w.mAttrs.getTitle()); @@ -6039,6 +6111,7 @@ public class WindowManagerService extends IWindowManager.Stub return result; } + // TODO(multidisplay): Extend to multiple displays. /** * Returns the focused window in the following format: * windowHashCodeInHexadecimal windowName @@ -6224,15 +6297,14 @@ public class WindowManagerService extends IWindowManager.Stub private WindowState findWindow(int hashCode) { if (hashCode == -1) { + // TODO(multidisplay): Extend to multiple displays. return getFocusedWindow(); } synchronized (mWindowMap) { - final ArrayList<WindowState> windows = mWindows; - final int count = windows.size(); - - for (int i = 0; i < count; i++) { - WindowState w = windows.get(i); + final AllWindowsIterator iterator = new AllWindowsIterator(); + while (iterator.hasNext()) { + final WindowState w = iterator.next(); if (System.identityHashCode(w) == hashCode) { return w; } @@ -6274,20 +6346,20 @@ public class WindowManagerService extends IWindowManager.Stub return config; } - private void adjustDisplaySizeRanges(int rotation, int dw, int dh) { + private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) { final int width = mPolicy.getConfigDisplayWidth(dw, dh, rotation); - if (width < mSmallestDisplayWidth) { - mSmallestDisplayWidth = width; + if (width < displayInfo.smallestNominalAppWidth) { + displayInfo.smallestNominalAppWidth = width; } - if (width > mLargestDisplayWidth) { - mLargestDisplayWidth = width; + if (width > displayInfo.largestNominalAppWidth) { + displayInfo.largestNominalAppWidth = width; } final int height = mPolicy.getConfigDisplayHeight(dw, dh, rotation); - if (height < mSmallestDisplayHeight) { - mSmallestDisplayHeight = height; + if (height < displayInfo.smallestNominalAppHeight) { + displayInfo.smallestNominalAppHeight = height; } - if (height > mLargestDisplayHeight) { - mLargestDisplayHeight = height; + if (height > displayInfo.largestNominalAppHeight) { + displayInfo.largestNominalAppHeight = height; } } @@ -6370,8 +6442,8 @@ public class WindowManagerService extends IWindowManager.Stub return curLayout; } - private void computeSizeRangesAndScreenLayout(boolean rotated, int dw, int dh, - float density, Configuration outConfig) { + private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated, + int dw, int dh, float density, Configuration outConfig) { // We need to determine the smallest width that will occur under normal // operation. To this, start with the base screen size and compute the // width under the different possible rotations. We need to un-rotate @@ -6384,21 +6456,21 @@ public class WindowManagerService extends IWindowManager.Stub unrotDw = dw; unrotDh = dh; } - mSmallestDisplayWidth = 1<<30; - mSmallestDisplayHeight = 1<<30; - mLargestDisplayWidth = 0; - mLargestDisplayHeight = 0; - adjustDisplaySizeRanges(Surface.ROTATION_0, unrotDw, unrotDh); - adjustDisplaySizeRanges(Surface.ROTATION_90, unrotDh, unrotDw); - adjustDisplaySizeRanges(Surface.ROTATION_180, unrotDw, unrotDh); - adjustDisplaySizeRanges(Surface.ROTATION_270, unrotDh, unrotDw); + displayInfo.smallestNominalAppWidth = 1<<30; + displayInfo.smallestNominalAppHeight = 1<<30; + displayInfo.largestNominalAppWidth = 0; + displayInfo.largestNominalAppHeight = 0; + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, unrotDw, unrotDh); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, unrotDh, unrotDw); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, unrotDw, unrotDh); + adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, unrotDh, unrotDw); int sl = Configuration.SCREENLAYOUT_SIZE_XLARGE | Configuration.SCREENLAYOUT_LONG_YES; sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh); sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw); sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh); sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw); - outConfig.smallestScreenWidthDp = (int)(mSmallestDisplayWidth / density); + outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density); outConfig.screenLayout = sl; } @@ -6433,42 +6505,39 @@ public class WindowManagerService extends IWindowManager.Stub } boolean computeScreenConfigurationLocked(Configuration config) { - if (mDisplay == null) { + if (mDefaultDisplay == null) { return false; } + // TODO(multidisplay): For now, apply Configuration to main screen only. + final DisplayContent displayContent = getDefaultDisplayContent(); + // Use the effective "visual" dimensions based on current rotation final boolean rotated = (mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270); - final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; - final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; - - synchronized(mDisplaySizeLock) { - if (mAltOrientation) { - mCurDisplayWidth = realdw; - mCurDisplayHeight = realdh; - if (realdw > realdh) { - // Turn landscape into portrait. - int maxw = (int)(realdh/1.3f); - if (maxw < realdw) { - mCurDisplayWidth = maxw; - } - } else { - // Turn portrait into landscape. - int maxh = (int)(realdw/1.3f); - if (maxh < realdh) { - mCurDisplayHeight = maxh; - } + final int realdw = rotated ? + displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth; + final int realdh = rotated ? + displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight; + int dw = realdw; + int dh = realdh; + + if (mAltOrientation) { + if (realdw > realdh) { + // Turn landscape into portrait. + int maxw = (int)(realdh/1.3f); + if (maxw < realdw) { + dw = maxw; } } else { - mCurDisplayWidth = realdw; - mCurDisplayHeight = realdh; + // Turn portrait into landscape. + int maxh = (int)(realdw/1.3f); + if (maxh < realdh) { + dh = maxh; + } } } - final int dw = mCurDisplayWidth; - final int dh = mCurDisplayHeight; - if (config != null) { int orientation = Configuration.ORIENTATION_SQUARE; if (dw < dh) { @@ -6479,25 +6548,28 @@ public class WindowManagerService extends IWindowManager.Stub config.orientation = orientation; } - // Update real display metrics. - mDisplay.getMetricsWithSize(mRealDisplayMetrics, mCurDisplayWidth, mCurDisplayHeight); - // Update application display metrics. - final DisplayMetrics dm = mDisplayMetrics; final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); - synchronized(mDisplaySizeLock) { - mAppDisplayWidth = appWidth; - mAppDisplayHeight = appHeight; - mAnimator.setDisplayDimensions(mCurDisplayWidth, mCurDisplayHeight, - mAppDisplayWidth, mAppDisplayHeight); + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + synchronized(displayContent.mDisplaySizeLock) { + displayInfo.rotation = mRotation; + displayInfo.logicalWidth = dw; + displayInfo.logicalHeight = dh; + displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity; + displayInfo.appWidth = appWidth; + displayInfo.appHeight = appHeight; + displayInfo.getLogicalMetrics(mRealDisplayMetrics, null); + displayInfo.getAppMetrics(mDisplayMetrics, null); + mDisplayManagerService.setDisplayInfo(displayContent.getDisplayId(), displayInfo); + + mAnimator.setDisplayDimensions(dw, dh, appWidth, appHeight); } if (false) { - Slog.i(TAG, "Set app display size: " + mAppDisplayWidth - + " x " + mAppDisplayHeight); + Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight); } - mDisplay.getMetricsWithSize(dm, mAppDisplayWidth, mAppDisplayHeight); + final DisplayMetrics dm = mDisplayMetrics; mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm, mCompatDisplayMetrics); @@ -6506,11 +6578,12 @@ public class WindowManagerService extends IWindowManager.Stub / dm.density); config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation) / dm.density); - computeSizeRangesAndScreenLayout(rotated, dw, dh, dm.density, config); + computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh, dm.density, config); config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale); config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale); config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh); + config.densityDpi = displayContent.mBaseDisplayDensity; // Update the configuration based on available input devices, lid switch, // and platform configuration. @@ -6638,7 +6711,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { try { if (mDragState == null) { - Surface surface = new Surface(session, callerPid, "drag surface", 0, + Surface surface = new Surface(session, callerPid, "drag surface", + mDefaultDisplay.getLayerStack(), width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN); if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG " + surface + ": CREATE"); @@ -6720,8 +6794,11 @@ public class WindowManagerService extends IWindowManager.Stub } } - // TODO: Put this on the IWindowManagerService and guard with a permission. - public IBinder getFocusedWindowClientToken() { + public IBinder getFocusedWindowToken() { + if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, + "getFocusedWindowToken()")) { + throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); + } synchronized (mWindowMap) { WindowState windowState = getFocusedWindowLocked(); if (windowState != null) { @@ -6731,8 +6808,11 @@ public class WindowManagerService extends IWindowManager.Stub } } - // TODO: This is a workaround - remove when 6623031 is fixed. public boolean getWindowFrame(IBinder token, Rect outBounds) { + if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, + "getWindowFrame()")) { + throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); + } synchronized (mWindowMap) { WindowState windowState = mWindowMap.get(token); if (windowState != null) { @@ -6790,45 +6870,57 @@ public class WindowManagerService extends IWindowManager.Stub } public void displayReady() { + WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); + final Display display = wm.getDefaultDisplay(); + displayReady(display.getDisplayId()); + synchronized(mWindowMap) { - if (mDisplay != null) { - throw new IllegalStateException("Display already initialized"); - } - WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); - mDisplay = wm.getDefaultDisplay(); + readForcedDisplaySizeAndDensityLocked(getDefaultDisplayContent()); + + mDefaultDisplay = display; mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_TOUCHSCREEN); - synchronized(mDisplaySizeLock) { - mInitialDisplayWidth = mDisplay.getRawWidth(); - mInitialDisplayHeight = mDisplay.getRawHeight(); - int rot = mDisplay.getRotation(); - if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { - // If the screen is currently rotated, we need to swap the - // initial width and height to get the true natural values. - int tmp = mInitialDisplayWidth; - mInitialDisplayWidth = mInitialDisplayHeight; - mInitialDisplayHeight = tmp; - } - mBaseDisplayWidth = mCurDisplayWidth = mAppDisplayWidth = mInitialDisplayWidth; - mBaseDisplayHeight = mCurDisplayHeight = mAppDisplayHeight = mInitialDisplayHeight; - mAnimator.setDisplayDimensions(mCurDisplayWidth, mCurDisplayHeight, - mAppDisplayWidth, mAppDisplayHeight); - } - mInputManager.setDisplaySize(Display.DEFAULT_DISPLAY, - mDisplay.getRawWidth(), mDisplay.getRawHeight(), - mDisplay.getRawExternalWidth(), mDisplay.getRawExternalHeight()); - mInputManager.setDisplayOrientation(Display.DEFAULT_DISPLAY, - mDisplay.getRotation(), mDisplay.getExternalRotation()); - mPolicy.setInitialDisplaySize(mDisplay, mInitialDisplayWidth, mInitialDisplayHeight); + PackageManager.FEATURE_TOUCHSCREEN); + + mAnimator.initializeLocked(display.getLayerStack()); + + final DisplayInfo displayInfo = getDefaultDisplayInfo(); + mAnimator.setDisplayDimensions(displayInfo.logicalWidth, displayInfo.logicalHeight, + displayInfo.appWidth, displayInfo.appHeight); + + DisplayDeviceInfo info = new DisplayDeviceInfo(); + mDisplayManagerService.getDefaultExternalDisplayDeviceInfo(info); + + final DisplayContent displayContent = getDefaultDisplayContent(); + mInputManager.setDisplaySize(displayContent.getDisplayId(), + displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight, + info.width, info.height); + mInputManager.setDisplayOrientation(displayContent.getDisplayId(), + mDefaultDisplay.getRotation(), Surface.ROTATION_0); + mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mInitialDisplayWidth, + displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity); } try { mActivityManager.updateConfiguration(null); } catch (RemoteException e) { } - - synchronized (mWindowMap) { - readForcedDisplaySizeLocked(); + } + + public void displayReady(int displayId) { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContent(displayId); + final DisplayInfo displayInfo; + synchronized(displayContent.mDisplaySizeLock) { + // Bootstrap the default logical display from the display manager. + displayInfo = displayContent.getDisplayInfo(); + mDisplayManagerService.getDisplayInfo(displayId, displayInfo); + displayContent.mInitialDisplayWidth = displayInfo.logicalWidth; + displayContent.mInitialDisplayHeight = displayInfo.logicalHeight; + displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi; + displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth; + displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight; + displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity; + } } } @@ -6836,14 +6928,13 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.systemReady(); } + // TODO(multidisplay): Call isScreenOn for each display. private void sendScreenStatusToClientsLocked() { - final ArrayList<WindowState> windows = mWindows; - final int count = windows.size(); - boolean on = mPowerManager.isScreenOn(); - for (int i = count - 1; i >= 0; i--) { - WindowState win = mWindows.get(i); + final boolean on = mPowerManager.isScreenOn(); + final AllWindowsIterator iterator = new AllWindowsIterator(); + while (iterator.hasNext()) { try { - win.mClient.dispatchScreenState(on); + iterator.next().mClient.dispatchScreenState(on); } catch (RemoteException e) { // Ignored } @@ -6864,7 +6955,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int REPORT_APPLICATION_TOKEN_WINDOWS = 8; public static final int REPORT_APPLICATION_TOKEN_DRAWN = 9; public static final int WINDOW_FREEZE_TIMEOUT = 11; - public static final int HOLD_SCREEN_CHANGED = 12; + public static final int APP_TRANSITION_TIMEOUT = 13; public static final int PERSIST_ANIMATION_SCALE = 14; public static final int FORCE_GC = 15; @@ -6877,17 +6968,13 @@ public class WindowManagerService extends IWindowManager.Stub public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22; public static final int BOOT_TIMEOUT = 23; public static final int WAITING_FOR_DRAWN_TIMEOUT = 24; - public static final int BULK_UPDATE_PARAMETERS = 25; + public static final int UPDATE_ANIM_PARAMETERS = 25; public static final int SHOW_STRICT_MODE_VIOLATION = 26; public static final int DO_ANIMATION_CALLBACK = 27; public static final int ANIMATOR_WHAT_OFFSET = 100000; public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1; - public static final int SET_WALLPAPER_OFFSET = ANIMATOR_WHAT_OFFSET + 2; - public static final int SET_DIM_PARAMETERS = ANIMATOR_WHAT_OFFSET + 3; - public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 4; - - private Session mLastReportedHold; + public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 2; public H() { } @@ -7122,12 +7209,14 @@ public class WindowManagerService extends IWindowManager.Stub } break; case WINDOW_FREEZE_TIMEOUT: { + // TODO(multidisplay): Can non-default displays rotate? synchronized (mWindowMap) { Slog.w(TAG, "Window freeze timeout expired."); - int i = mWindows.size(); + final WindowList windows = getDefaultWindowList(); + int i = windows.size(); while (i > 0) { i--; - WindowState w = mWindows.get(i); + WindowState w = windows.get(i); if (w.mOrientationChanging) { w.mOrientationChanging = false; Slog.w(TAG, "Force clearing orientation change: " + w); @@ -7138,33 +7227,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case HOLD_SCREEN_CHANGED: { - Session oldHold; - Session newHold; - synchronized (mWindowMap) { - oldHold = mLastReportedHold; - newHold = (Session)msg.obj; - mLastReportedHold = newHold; - } - - if (oldHold != newHold) { - try { - if (oldHold != null) { - mBatteryStats.noteStopWakelock(oldHold.mUid, -1, - "window", - BatteryStats.WAKE_TYPE_WINDOW); - } - if (newHold != null) { - mBatteryStats.noteStartWakelock(newHold.mUid, -1, - "window", - BatteryStats.WAKE_TYPE_WINDOW); - } - } catch (RemoteException e) { - } - } - break; - } - case APP_TRANSITION_TIMEOUT: { synchronized (mWindowMap) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { @@ -7191,18 +7253,22 @@ public class WindowManagerService extends IWindowManager.Stub } case FORCE_GC: { - synchronized(mWindowMap) { - if (mAnimationScheduled) { - // If we are animating, don't do the gc now but - // delay a bit so we don't interrupt the animation. - mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC), - 2000); - return; - } - // If we are currently rotating the display, it will - // schedule a new message when done. - if (mDisplayFrozen) { - return; + synchronized (mWindowMap) { + synchronized (mAnimator) { + // Since we're holding both mWindowMap and mAnimator we don't need to + // hold mAnimator.mLayoutToAnim. + if (mAnimator.mAnimating || mLayoutToAnim.mAnimationScheduled) { + // If we are animating, don't do the gc now but + // delay a bit so we don't interrupt the animation. + mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC), + 2000); + return; + } + // If we are currently rotating the display, it will + // schedule a new message when done. + if (mDisplayFrozen) { + return; + } } } Runtime.getRuntime().gc(); @@ -7306,42 +7372,10 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case BULK_UPDATE_PARAMETERS: { + case UPDATE_ANIM_PARAMETERS: { // Used to send multiple changes from the animation side to the layout side. synchronized (mWindowMap) { - boolean doRequest = false; - // TODO(cmautner): As the number of bits grows, use masks of bit groups to - // eliminate unnecessary tests. - if ((msg.arg1 & LayoutFields.SET_UPDATE_ROTATION) != 0) { - mInnerFields.mUpdateRotation = true; - doRequest = true; - } - if ((msg.arg1 & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) { - mInnerFields.mWallpaperMayChange = true; - doRequest = true; - } - if ((msg.arg1 & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) { - mInnerFields.mWallpaperForceHidingChanged = true; - doRequest = true; - } - if ((msg.arg1 & LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE) != 0) { - mInnerFields.mOrientationChangeComplete = false; - } else { - mInnerFields.mOrientationChangeComplete = true; - if (mWindowsFreezingScreen) { - doRequest = true; - } - } - if ((msg.arg1 & LayoutFields.SET_TURN_ON_SCREEN) != 0) { - mTurnOnScreen = true; - } - - mPendingLayoutChanges |= msg.arg2; - if (mPendingLayoutChanges != 0) { - doRequest = true; - } - - if (doRequest) { + if (copyAnimToLayoutParamsLocked()) { mH.sendEmptyMessage(CLEAR_PENDING_ACTIONS); performLayoutAndPlaceSurfacesLocked(); } @@ -7363,21 +7397,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case SET_WALLPAPER_OFFSET: { - final WindowStateAnimator winAnimator = (WindowStateAnimator) msg.obj; - winAnimator.setWallpaperOffset(msg.arg1, msg.arg2); - - scheduleAnimationLocked(); - break; - } - - case SET_DIM_PARAMETERS: { - mAnimator.mDimParams = (DimAnimator.Parameters) msg.obj; - - scheduleAnimationLocked(); - break; - } - case CLEAR_PENDING_ACTIONS: { mAnimator.clearPendingActions(); break; @@ -7416,13 +7435,13 @@ public class WindowManagerService extends IWindowManager.Stub // The focus for the client is the window immediately below // where we would place the input method window. int idx = findDesiredInputMethodWindowIndexLocked(false); - WindowState imFocus; if (idx > 0) { - imFocus = mWindows.get(idx-1); + // TODO(multidisplay): IMEs are only supported on the default display. + WindowState imFocus = getDefaultWindowList().get(idx-1); if (DEBUG_INPUT_METHOD) { Slog.i(TAG, "Desired input method target: " + imFocus); - Slog.i(TAG, "Current focus: " + this.mCurrentFocus); - Slog.i(TAG, "Last focus: " + this.mLastFocus); + Slog.i(TAG, "Current focus: " + mCurrentFocus); + Slog.i(TAG, "Last focus: " + mLastFocus); } if (imFocus != null) { // This may be a starting window, in which case we still want @@ -7452,141 +7471,191 @@ public class WindowManagerService extends IWindowManager.Stub imFocus.mSession.mClient.asBinder() == client.asBinder()) { return true; } - - // Okay, how about this... what is the current focus? - // It seems in some cases we may not have moved the IM - // target window, such as when it was in a pop-up window, - // so let's also look at the current focus. (An example: - // go to Gmail, start searching so the keyboard goes up, - // press home. Sometimes the IME won't go down.) - // Would be nice to fix this more correctly, but it's - // way at the end of a release, and this should be good enough. - if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null && - mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) { - return true; - } } } - } - return false; - } - - public void getDisplaySize(Point size) { - synchronized(mDisplaySizeLock) { - size.x = mAppDisplayWidth; - size.y = mAppDisplayHeight; - } - } - - public void getRealDisplaySize(Point size) { - synchronized(mDisplaySizeLock) { - size.x = mCurDisplayWidth; - size.y = mCurDisplayHeight; - } - } - public void getInitialDisplaySize(Point size) { - synchronized(mDisplaySizeLock) { - size.x = mInitialDisplayWidth; - size.y = mInitialDisplayHeight; - } - } - - public int getMaximumSizeDimension() { - synchronized(mDisplaySizeLock) { - // Do this based on the raw screen size, until we are smarter. - return mBaseDisplayWidth > mBaseDisplayHeight - ? mBaseDisplayWidth : mBaseDisplayHeight; + // Okay, how about this... what is the current focus? + // It seems in some cases we may not have moved the IM + // target window, such as when it was in a pop-up window, + // so let's also look at the current focus. (An example: + // go to Gmail, start searching so the keyboard goes up, + // press home. Sometimes the IME won't go down.) + // Would be nice to fix this more correctly, but it's + // way at the end of a release, and this should be good enough. + if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null + && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) { + return true; + } } + return false; } - public void getCurrentSizeRange(Point smallestSize, Point largestSize) { - synchronized(mDisplaySizeLock) { - smallestSize.x = mSmallestDisplayWidth; - smallestSize.y = mSmallestDisplayHeight; - largestSize.x = mLargestDisplayWidth; - largestSize.y = mLargestDisplayHeight; + public void getInitialDisplaySize(int displayId, Point size) { + // TODO(cmautner): Access to DisplayContent should be locked on mWindowMap. Doing that + // could lead to deadlock since this is called from ActivityManager. + final DisplayContent displayContent = getDisplayContent(displayId); + synchronized(displayContent.mDisplaySizeLock) { + size.x = displayContent.mInitialDisplayWidth; + size.y = displayContent.mInitialDisplayHeight; } } - public void setForcedDisplaySize(int longDimen, int shortDimen) { + public void setForcedDisplaySize(int displayId, int longDimen, int shortDimen) { synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContent(displayId); int width, height; - if (mInitialDisplayWidth < mInitialDisplayHeight) { - width = shortDimen < mInitialDisplayWidth - ? shortDimen : mInitialDisplayWidth; - height = longDimen < mInitialDisplayHeight - ? longDimen : mInitialDisplayHeight; + if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) { + width = shortDimen < displayContent.mInitialDisplayWidth + ? shortDimen : displayContent.mInitialDisplayWidth; + height = longDimen < displayContent.mInitialDisplayHeight + ? longDimen : displayContent.mInitialDisplayHeight; } else { - width = longDimen < mInitialDisplayWidth - ? longDimen : mInitialDisplayWidth; - height = shortDimen < mInitialDisplayHeight - ? shortDimen : mInitialDisplayHeight; + width = longDimen < displayContent.mInitialDisplayWidth + ? longDimen : displayContent.mInitialDisplayWidth; + height = shortDimen < displayContent.mInitialDisplayHeight + ? shortDimen : displayContent.mInitialDisplayHeight; } - setForcedDisplaySizeLocked(width, height); + setForcedDisplaySizeLocked(displayContent, width, height); Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DISPLAY_SIZE_FORCED, width + "," + height); } } - private void rebuildBlackFrame() { + private void rebuildBlackFrameLocked() { if (mBlackFrame != null) { mBlackFrame.kill(); mBlackFrame = null; } - if (mBaseDisplayWidth < mInitialDisplayWidth - || mBaseDisplayHeight < mInitialDisplayHeight) { + // TODO(multidisplay): For now rotations are only main screen. + final DisplayContent displayContent = getDefaultDisplayContent(); + if (displayContent.mBaseDisplayWidth < displayContent.mInitialDisplayWidth + || displayContent.mBaseDisplayHeight < displayContent.mInitialDisplayHeight) { int initW, initH, baseW, baseH; final boolean rotated = (mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270); + if (DEBUG_BOOT) { + Slog.i(TAG, "BLACK FRAME: rotated=" + rotated + " init=" + + displayContent.mInitialDisplayWidth + "x" + + displayContent.mInitialDisplayHeight + " base=" + + displayContent.mBaseDisplayWidth + "x" + + displayContent.mBaseDisplayHeight); + } if (rotated) { - initW = mInitialDisplayHeight; - initH = mInitialDisplayWidth; - baseW = mBaseDisplayHeight; - baseH = mBaseDisplayWidth; + initW = displayContent.mInitialDisplayHeight; + initH = displayContent.mInitialDisplayWidth; + baseW = displayContent.mBaseDisplayHeight; + baseH = displayContent.mBaseDisplayWidth; } else { - initW = mInitialDisplayWidth; - initH = mInitialDisplayHeight; - baseW = mBaseDisplayWidth; - baseH = mBaseDisplayHeight; + initW = displayContent.mInitialDisplayWidth; + initH = displayContent.mInitialDisplayHeight; + baseW = displayContent.mBaseDisplayWidth; + baseH = displayContent.mBaseDisplayHeight; } Rect outer = new Rect(0, 0, initW, initH); Rect inner = new Rect(0, 0, baseW, baseH); try { - mBlackFrame = new BlackFrame(mFxSession, outer, inner, MASK_LAYER); + mBlackFrame = new BlackFrame(mFxSession, outer, inner, MASK_LAYER, + mDefaultDisplay.getLayerStack()); } catch (Surface.OutOfResourcesException e) { } } } - private void readForcedDisplaySizeLocked() { - final String str = Settings.Secure.getString(mContext.getContentResolver(), + private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) { + boolean changed = false; + final String sizeStr = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.DISPLAY_SIZE_FORCED); - if (str == null || str.length() == 0) { - return; + if (sizeStr != null && sizeStr.length() > 0) { + final int pos = sizeStr.indexOf(','); + if (pos > 0 && sizeStr.lastIndexOf(',') == pos) { + int width, height; + try { + width = Integer.parseInt(sizeStr.substring(0, pos)); + height = Integer.parseInt(sizeStr.substring(pos+1)); + synchronized(displayContent.mDisplaySizeLock) { + if (displayContent.mBaseDisplayWidth != width + || displayContent.mBaseDisplayHeight != height) { + changed = true; + Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height); + displayContent.mBaseDisplayWidth = width; + displayContent.mBaseDisplayHeight = height; + } + } + } catch (NumberFormatException ex) { + } + } } - final int pos = str.indexOf(','); - if (pos <= 0 || str.lastIndexOf(',') != pos) { - return; + final String densityStr = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DISPLAY_DENSITY_FORCED); + if (densityStr != null && densityStr.length() > 0) { + int density; + try { + density = Integer.parseInt(densityStr); + synchronized(displayContent.mDisplaySizeLock) { + if (displayContent.mBaseDisplayDensity != density) { + changed = true; + Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density); + displayContent.mBaseDisplayDensity = density; + } + } + } catch (NumberFormatException ex) { + } } - int width, height; - try { - width = Integer.parseInt(str.substring(0, pos)); - height = Integer.parseInt(str.substring(pos+1)); - } catch (NumberFormatException ex) { - return; + if (changed) { + rebuildBlackFrameLocked(); } - setForcedDisplaySizeLocked(width, height); } - private void setForcedDisplaySizeLocked(int width, int height) { + private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) { Slog.i(TAG, "Using new display size: " + width + "x" + height); - synchronized(mDisplaySizeLock) { - mBaseDisplayWidth = width; - mBaseDisplayHeight = height; + synchronized(displayContent.mDisplaySizeLock) { + displayContent.mBaseDisplayWidth = width; + displayContent.mBaseDisplayHeight = height; } - mPolicy.setInitialDisplaySize(mDisplay, mBaseDisplayWidth, mBaseDisplayHeight); + reconfigureDisplayLocked(displayContent); + } + + public void clearForcedDisplaySize(int displayId) { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContent(displayId); + setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth, + displayContent.mInitialDisplayHeight); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DISPLAY_SIZE_FORCED, ""); + } + } + + public void setForcedDisplayDensity(int displayId, int density) { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContent(displayId); + setForcedDisplayDensityLocked(displayContent, density); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DISPLAY_DENSITY_FORCED, Integer.toString(density)); + } + } + + private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) { + Slog.i(TAG, "Using new display density: " + density); + + synchronized(displayContent.mDisplaySizeLock) { + displayContent.mBaseDisplayDensity = density; + } + reconfigureDisplayLocked(displayContent); + } + + public void clearForcedDisplayDensity(int displayId) { + synchronized(mWindowMap) { + final DisplayContent displayContent = getDisplayContent(displayId); + setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DISPLAY_DENSITY_FORCED, ""); + } + } + + private void reconfigureDisplayLocked(DisplayContent displayContent) { + mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mBaseDisplayWidth, + displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity); mLayoutNeeded = true; @@ -7605,19 +7674,11 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } - rebuildBlackFrame(); + rebuildBlackFrameLocked(); performLayoutAndPlaceSurfacesLocked(); } - public void clearForcedDisplaySize() { - synchronized(mWindowMap) { - setForcedDisplaySizeLocked(mInitialDisplayWidth, mInitialDisplayHeight); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.DISPLAY_SIZE_FORCED, ""); - } - } - public boolean hasSystemNavBar() { return mPolicy.hasSystemNavBar(); } @@ -7660,9 +7721,17 @@ public class WindowManagerService extends IWindowManager.Stub } final void rebuildAppWindowListLocked() { - int NW = mWindows.size(); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + rebuildAppWindowListLocked(iterator.next()); + } + } + + private void rebuildAppWindowListLocked(final DisplayContent displayContent) { + final WindowList windows = displayContent.getWindowList(); + int NW = windows.size(); int i; - int lastWallpaper = -1; + int lastBelow = -1; int numRemoved = 0; if (mRebuildTmp.length < NW) { @@ -7672,9 +7741,9 @@ public class WindowManagerService extends IWindowManager.Stub // First remove all existing app windows. i=0; while (i < NW) { - WindowState w = mWindows.get(i); + WindowState w = windows.get(i); if (w.mAppToken != null) { - WindowState win = mWindows.remove(i); + WindowState win = windows.remove(i); win.mRebuilding = true; mRebuildTmp[numRemoved] = win; mWindowsChanged = true; @@ -7683,17 +7752,19 @@ public class WindowManagerService extends IWindowManager.Stub NW--; numRemoved++; continue; - } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER - && lastWallpaper == i-1) { - lastWallpaper = i; + } else if (lastBelow == i-1) { + if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER + || w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) { + lastBelow = i; + } } i++; } - // The wallpaper window(s) typically live at the bottom of the stack, - // so skip them before adding app tokens. - lastWallpaper++; - i = lastWallpaper; + // Keep whatever windows were below the app windows still below, + // by skipping them. + lastBelow++; + i = lastBelow; // First add all of the exiting app tokens... these are no longer // in the main app list, but still have windows shown. We put them @@ -7701,16 +7772,16 @@ public class WindowManagerService extends IWindowManager.Stub // will care about them. int NT = mExitingAppTokens.size(); for (int j=0; j<NT; j++) { - i = reAddAppWindowsLocked(i, mExitingAppTokens.get(j)); + i = reAddAppWindowsLocked(displayContent, i, mExitingAppTokens.get(j)); } // And add in the still active app tokens in Z order. NT = mAnimatingAppTokens.size(); for (int j=0; j<NT; j++) { - i = reAddAppWindowsLocked(i, mAnimatingAppTokens.get(j)); + i = reAddAppWindowsLocked(displayContent, i, mAnimatingAppTokens.get(j)); } - i -= lastWallpaper; + i -= lastBelow; if (i != numRemoved) { Slog.w(TAG, "Rebuild removed " + numRemoved + " windows but added " + i); @@ -7733,8 +7804,8 @@ public class WindowManagerService extends IWindowManager.Stub } } - private final void assignLayersLocked() { - int N = mWindows.size(); + private final void assignLayersLocked(WindowList windows) { + int N = windows.size(); int curBaseLayer = 0; int curLayer = 0; int i; @@ -7746,7 +7817,7 @@ public class WindowManagerService extends IWindowManager.Stub } for (i=0; i<N; i++) { - final WindowState w = mWindows.get(i); + final WindowState w = windows.get(i); final WindowStateAnimator winAnimator = w.mWinAnimator; boolean layerChanged = false; int oldLayer = w.mLayer; @@ -7781,7 +7852,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (layerChanged && mAnimator.isDimming(winAnimator)) { // Force an animation pass just to update the mDimAnimator layer. - scheduleAnimationLocked(); + updateLayoutToAnimationLocked(); } if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": " + winAnimator.mAnimLayer); @@ -7808,7 +7879,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mDisplay == null) { + if (mDefaultDisplay == null) { // Not yet initialized, nothing to do. return; } @@ -7839,34 +7910,32 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RuntimeException e) { Log.wtf(TAG, "Unhandled exception while force removing for memory", e); } - + try { - performLayoutAndPlaceSurfacesLockedInner(recoveringMemory); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + final DisplayContent displayContent = iterator.next(); + performLayoutAndPlaceSurfacesLockedInner(displayContent, recoveringMemory); + + final int N = mPendingRemove.size(); + if (N > 0) { + if (mPendingRemoveTmp.length < N) { + mPendingRemoveTmp = new WindowState[N+10]; + } + mPendingRemove.toArray(mPendingRemoveTmp); + mPendingRemove.clear(); + for (int i=0; i<N; i++) { + WindowState w = mPendingRemoveTmp[i]; + removeWindowInnerLocked(w.mSession, w); + } - final int N = mPendingRemove.size(); - if (N > 0) { - if (mPendingRemoveTmp.length < N) { - mPendingRemoveTmp = new WindowState[N+10]; - } - mPendingRemove.toArray(mPendingRemoveTmp); - mPendingRemove.clear(); - for (int i=0; i<N; i++) { - WindowState w = mPendingRemoveTmp[i]; - removeWindowInnerLocked(w.mSession, w); + assignLayersLocked(displayContent.getWindowList()); + mLayoutNeeded = true; } - - mInLayout = false; - assignLayersLocked(); - mLayoutNeeded = true; - // XXX this recursion seems broken! - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - performLayoutAndPlaceSurfacesLocked(); - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout"); - - } else { - mInLayout = false; } + mInLayout = false; + if (mLayoutNeeded) { if (++mLayoutRepeatCount < 6) { requestTraversalLocked(); @@ -7890,22 +7959,24 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } - private final void performLayoutLockedInner(boolean initial, boolean updateInputWindows) { + private final void performLayoutLockedInner(final DisplayContent displayContent, + boolean initial, boolean updateInputWindows) { if (!mLayoutNeeded) { return; } - + WindowList windows = displayContent.getWindowList(); mLayoutNeeded = false; - - final int dw = mCurDisplayWidth; - final int dh = mCurDisplayHeight; + + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; final int NFW = mFakeWindows.size(); for (int i=0; i<NFW; i++) { mFakeWindows.get(i).layout(dw, dh); } - final int N = mWindows.size(); + final int N = windows.size(); int i; if (DEBUG_LAYOUT) { @@ -7913,9 +7984,12 @@ public class WindowManagerService extends IWindowManager.Stub Slog.v(TAG, "performLayout: needed=" + mLayoutNeeded + " dw=" + dw + " dh=" + dh); } - + + WindowStateAnimator universeBackground = null; + mPolicy.beginLayoutLw(dw, dh, mRotation); mSystemDecorLayer = mPolicy.getSystemDecorRectLw(mSystemDecorRect); + mScreenRect.set(0, 0, dw, dh); int seq = mLayoutSeq+1; if (seq < 0) seq = 0; @@ -7925,7 +7999,7 @@ public class WindowManagerService extends IWindowManager.Stub // to another window). int topAttached = -1; for (i = N-1; i >= 0; i--) { - final WindowState win = mWindows.get(i); + final WindowState win = windows.get(i); // Don't do layout of a window if it is not visible, or // soon won't be visible, to avoid wasting time and funky @@ -7956,7 +8030,8 @@ 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 (!gone || !win.mHaveFrame || win.mLayoutNeeded) { + if (!gone || !win.mHaveFrame || win.mLayoutNeeded + || win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) { if (!win.mLayoutAttached) { if (initial) { //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); @@ -7974,6 +8049,16 @@ public class WindowManagerService extends IWindowManager.Stub if (topAttached < 0) topAttached = i; } } + if (win.mViewVisibility == View.VISIBLE + && win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND + && universeBackground == null) { + universeBackground = win.mWinAnimator; + } + } + + if (mAnimator.mUniverseBackground != universeBackground) { + mFocusMayChange = true; + mAnimator.mUniverseBackground = universeBackground; } // Now perform layout of attached windows, which usually @@ -7981,7 +8066,7 @@ public class WindowManagerService extends IWindowManager.Stub // XXX does not deal with windows that are attached to windows // that are themselves attached. for (i = topAttached; i >= 0; i--) { - final WindowState win = mWindows.get(i); + final WindowState win = windows.get(i); if (win.mLayoutAttached) { if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win @@ -8042,10 +8127,11 @@ public class WindowManagerService extends IWindowManager.Stub /** * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method. + * @param windows TODO(cmautner): * * @return bitmap indicating if another pass through layout must be made. */ - public int handleAppTransitionReadyLocked() { + public int handleAppTransitionReadyLocked(WindowList windows) { int changes = 0; int i; int NN = mOpeningApps.size(); @@ -8200,15 +8286,23 @@ public class WindowManagerService extends IWindowManager.Stub NN = mOpeningApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mOpeningApps.get(i); + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); - wtoken.mAppAnimator.clearThumbnail(); + appAnimator.clearThumbnail(); wtoken.reportedVisible = false; wtoken.inPendingTransaction = false; - wtoken.mAppAnimator.animation = null; + appAnimator.animation = null; setTokenVisibilityLocked(wtoken, animLp, true, transit, false); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; - mAnimator.mAnimating |= wtoken.mAppAnimator.showAllWindowsLocked(); + + appAnimator.mAllAppWinAnimators.clear(); + final int N = wtoken.allAppWindows.size(); + for (int j = 0; j < N; j++) { + appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); + } + mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); + if (animLp != null) { int layer = -1; for (int j=0; j<wtoken.windows.size(); j++) { @@ -8249,7 +8343,8 @@ public class WindowManagerService extends IWindowManager.Stub mNextAppTransitionThumbnail.getHeight()); try { Surface surface = new Surface(mFxSession, Process.myPid(), - "thumbnail anim", 0, dirty.width(), dirty.height(), + "thumbnail anim", mDefaultDisplay.getLayerStack(), + dirty.width(), dirty.height(), PixelFormat.TRANSLUCENT, Surface.HIDDEN); topOpeningApp.mAppAnimator.thumbnail = surface; if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL " @@ -8262,7 +8357,7 @@ public class WindowManagerService extends IWindowManager.Stub drawSurface.release(); topOpeningApp.mAppAnimator.thumbnailLayer = topOpeningLayer; Animation anim = createThumbnailAnimationLocked( - transit, true, true, mNextAppTransitionDelayed); + transit, true, true, mNextAppTransitionScaleUp); topOpeningApp.mAppAnimator.thumbnailAnimation = anim; anim.restrictDuration(MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(mTransitionAnimationScale); @@ -8289,11 +8384,12 @@ public class WindowManagerService extends IWindowManager.Stub changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; mLayoutNeeded = true; - if (!moveInputMethodWindowsIfNeededLocked(true)) { - assignLayersLocked(); + + // TODO(multidisplay): IMEs are only supported on the default display. + if (windows == getDefaultWindowList() && !moveInputMethodWindowsIfNeededLocked(true)) { + assignLayersLocked(windows); } - updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, - false /*updateInputWindows*/); + updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, false /*updateInputWindows*/); mFocusMayChange = false; } @@ -8302,7 +8398,6 @@ public class WindowManagerService extends IWindowManager.Stub /** * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method. - * * @return bitmap indicating if another pass through layout must be made. */ private int handleAnimatingStoppedAndTransitionLocked() { @@ -8476,14 +8571,14 @@ public class WindowManagerService extends IWindowManager.Stub if (!mAnimator.isDimming(winAnimator)) { final int width, height; if (attrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) { - width = mCurDisplayWidth; - height = mCurDisplayHeight; + final DisplayInfo displayInfo = w.mDisplayContent.getDisplayInfo(); + width = displayInfo.logicalWidth; + height = displayInfo.logicalHeight; } else { width = innerDw; height = innerDh; } - mAnimator.startDimming(winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount, - width, height); + startDimming(winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount, width, height); } } } @@ -8511,21 +8606,23 @@ public class WindowManagerService extends IWindowManager.Stub // "Something has changed! Let's make it correct now." private final void performLayoutAndPlaceSurfacesLockedInner( - boolean recoveringMemory) { + final DisplayContent displayContent, boolean recoveringMemory) { if (DEBUG_WINDOW_TRACE) { Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by " + Debug.getCallers(3)); } - if (mDisplay == null) { + if (mDefaultDisplay == null) { Slog.i(TAG, "skipping performLayoutAndPlaceSurfacesLockedInner with no mDisplay"); return; } + final WindowList windows = displayContent.getWindowList(); final long currentTime = SystemClock.uptimeMillis(); - final int dw = mCurDisplayWidth; - final int dh = mCurDisplayHeight; - final int innerDw = mAppDisplayWidth; - final int innerDh = mAppDisplayHeight; + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; + final int innerDw = displayInfo.appWidth; + final int innerDh = displayInfo.appHeight; int i; @@ -8577,8 +8674,8 @@ public class WindowManagerService extends IWindowManager.Stub mPendingLayoutChanges); if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) { - if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { - assignLayersLocked(); + if ((adjustWallpaperWindowsLocked() & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { + assignLayersLocked(windows); mLayoutNeeded = true; } } @@ -8597,7 +8694,7 @@ public class WindowManagerService extends IWindowManager.Stub // FIRST LOOP: Perform a layout, if needed. if (repeats < 4) { - performLayoutLockedInner(repeats == 1, false /*updateInputWindows*/); + performLayoutLockedInner(displayContent, repeats == 1, false /*updateInputWindows*/); } else { Slog.w(TAG, "Layout repeat skipped after too many iterations"); } @@ -8608,8 +8705,8 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount, mPendingLayoutChanges); mPolicy.beginAnimationLw(dw, dh); - for (i = mWindows.size() - 1; i >= 0; i--) { - WindowState w = mWindows.get(i); + for (i = windows.size() - 1; i >= 0; i--) { + WindowState w = windows.get(i); if (w.mHasSurface) { mPolicy.animatingWindowLw(w, w.mAttrs); } @@ -8627,9 +8724,9 @@ public class WindowManagerService extends IWindowManager.Stub boolean focusDisplayed = false; boolean updateAllDrawn = false; - final int N = mWindows.size(); + final int N = windows.size(); for (i=N-1; i>=0; i--) { - WindowState w = mWindows.get(i); + WindowState w = windows.get(i); final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured; @@ -8660,6 +8757,10 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.setAnimation(a); winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left; winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top; + try { + w.mClient.moved(w.mFrame.left, w.mFrame.top); + } catch (RemoteException e) { + } } //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing"); @@ -8676,7 +8777,7 @@ public class WindowManagerService extends IWindowManager.Stub mInnerFields.mWallpaperMayChange = true; mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; if (WindowManagerService.DEBUG_LAYOUT_REPEATS) { - debugLayoutRepeats("updateWindowsAndWallpaperLocked 1", + debugLayoutRepeats("wallpaper and commitFinishDrawingLocked true", mPendingLayoutChanges); } } @@ -8690,7 +8791,8 @@ public class WindowManagerService extends IWindowManager.Stub + w.isOnScreen() + " allDrawn=" + atoken.allDrawn + " freezingScreen=" + atoken.mAppAnimator.freezingScreen); } - if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) { + if (atoken != null + && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) { if (atoken.lastTransactionSequence != mTransactionSequence) { atoken.lastTransactionSequence = mTransactionSequence; atoken.numInterestingWindows = atoken.numDrawnWindows = 0; @@ -8748,7 +8850,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (!mInnerFields.mDimming && mAnimator.isDimming()) { - mAnimator.stopDimming(); + stopDimming(); } } catch (RuntimeException e) { Log.wtf(TAG, "Unhandled exception in Window Manager", e); @@ -8760,7 +8862,7 @@ public class WindowManagerService extends IWindowManager.Stub // all of the app tokens to be shown and see if they are ready // to go. if (mAppTransitionReady) { - mPendingLayoutChanges |= handleAppTransitionReadyLocked(); + mPendingLayoutChanges |= handleAppTransitionReadyLocked(windows); if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked", mPendingLayoutChanges); } @@ -8803,7 +8905,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper layer changed: assigning layers + relayout"); mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; - assignLayersLocked(); + assignLayersLocked(windows); } else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility changed: relayout"); @@ -8847,9 +8949,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ORIENTATION && winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i( TAG, "Resizing " + win + " WITH DRAW PENDING"); - win.mClient.resized((int)winAnimator.mSurfaceW, - (int)winAnimator.mSurfaceH, - win.mLastContentInsets, win.mLastVisibleInsets, + win.mClient.resized(win.mFrame, win.mLastContentInsets, win.mLastVisibleInsets, winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING, configChanged ? win.mConfiguration : null); win.mContentInsetsChanged = false; @@ -8899,6 +8999,7 @@ public class WindowManagerService extends IWindowManager.Stub mExitingTokens.remove(i); if (token.windowType == TYPE_WALLPAPER) { mWallpaperTokens.remove(token); + updateLayoutToAnimWallpaperTokens(); } } } @@ -8940,31 +9041,25 @@ public class WindowManagerService extends IWindowManager.Stub // Finally update all input windows now that the window changes have stabilized. mInputMonitor.updateInputWindowsLw(true /*force*/); - setHoldScreenLocked(mInnerFields.mHoldScreen != null); + setHoldScreenLocked(mInnerFields.mHoldScreen); if (!mDisplayFrozen) { if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) { - mPowerManager.setScreenBrightnessOverride(-1); + mPowerManager.setScreenBrightnessOverrideFromWindowManager(-1); } else { - mPowerManager.setScreenBrightnessOverride((int) - (mInnerFields.mScreenBrightness * PowerManager.BRIGHTNESS_ON)); + mPowerManager.setScreenBrightnessOverrideFromWindowManager( + toBrightnessOverride(mInnerFields.mScreenBrightness)); } if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) { - mPowerManager.setButtonBrightnessOverride(-1); + mPowerManager.setButtonBrightnessOverrideFromWindowManager(-1); } else { - mPowerManager.setButtonBrightnessOverride((int) - (mInnerFields.mButtonBrightness * PowerManager.BRIGHTNESS_ON)); + mPowerManager.setButtonBrightnessOverrideFromWindowManager( + toBrightnessOverride(mInnerFields.mButtonBrightness)); } } - if (mInnerFields.mHoldScreen != mHoldingScreenOn) { - mHoldingScreenOn = mInnerFields.mHoldScreen; - Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, mInnerFields.mHoldScreen); - mH.sendMessage(m); - } if (mTurnOnScreen) { if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!"); - mPowerManager.userActivity(SystemClock.uptimeMillis(), false, - LocalPowerManager.BUTTON_EVENT, true); + mPowerManager.wakeUp(SystemClock.uptimeMillis()); mTurnOnScreen = false; } @@ -8986,7 +9081,7 @@ public class WindowManagerService extends IWindowManager.Stub // be enabled, because the window obscured flags have changed. enableScreenIfNeededLocked(); - scheduleAnimationLocked(); + updateLayoutToAnimationLocked(); if (DEBUG_WINDOW_TRACE) { Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges=" @@ -8995,6 +9090,10 @@ public class WindowManagerService extends IWindowManager.Stub } } + private int toBrightnessOverride(float value) { + return (int)(value * PowerManager.BRIGHTNESS_ON); + } + void checkDrawnWindowsLocked() { if (mWaitingForDrawn.size() > 0) { for (int j=mWaitingForDrawn.size()-1; j>=0; j--) { @@ -9040,13 +9139,17 @@ public class WindowManagerService extends IWindowManager.Stub } } - /** - * Must be called with the main window manager lock held. - */ - void setHoldScreenLocked(boolean holding) { - boolean state = mHoldingScreenWakeLock.isHeld(); - if (holding != state) { - if (holding) { + void setHoldScreenLocked(final Session newHoldScreen) { + final boolean hold = newHoldScreen != null; + + if (hold && mHoldingScreenOn != newHoldScreen) { + mHoldingScreenWakeLock.setWorkSource(new WorkSource(newHoldScreen.mUid)); + } + mHoldingScreenOn = newHoldScreen; + + final boolean state = mHoldingScreenWakeLock.isHeld(); + if (hold != state) { + if (hold) { mPolicy.screenOnStartedLw(); mHoldingScreenWakeLock.acquire(); } else { @@ -9063,13 +9166,118 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** Note that Locked in this case is on mLayoutToAnim */ void scheduleAnimationLocked() { - if (!mAnimationScheduled) { - mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationRunnable, null); - mAnimationScheduled = true; + final LayoutToAnimatorParams layoutToAnim = mLayoutToAnim; + if (!layoutToAnim.mAnimationScheduled) { + layoutToAnim.mAnimationScheduled = true; + mChoreographer.postCallback( + Choreographer.CALLBACK_ANIMATION, mAnimator.mAnimationRunnable, null); + } + } + + void updateLayoutToAnimationLocked() { + final LayoutToAnimatorParams layoutToAnim = mLayoutToAnim; + synchronized (layoutToAnim) { + // Copy local params to transfer params. + ArrayList<WinAnimatorList> allWinAnimatorLists = layoutToAnim.mWinAnimatorLists; + allWinAnimatorLists.clear(); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + final DisplayContent displayContent = iterator.next(); + WinAnimatorList winAnimatorList = new WinAnimatorList(); + final WindowList windows = displayContent.getWindowList(); + int N = windows.size(); + for (int i = 0; i < N; i++) { + final WindowStateAnimator winAnimator = windows.get(i).mWinAnimator; + if (winAnimator.mSurface != null) { + winAnimatorList.add(winAnimator); + } + } + allWinAnimatorLists.add(winAnimatorList); + } + + layoutToAnim.mWallpaperTarget = mWallpaperTarget; + layoutToAnim.mLowerWallpaperTarget = mLowerWallpaperTarget; + layoutToAnim.mUpperWallpaperTarget = mUpperWallpaperTarget; + + final ArrayList<AppWindowAnimParams> paramList = layoutToAnim.mAppWindowAnimParams; + paramList.clear(); + int N = mAnimatingAppTokens.size(); + for (int i = 0; i < N; i++) { + paramList.add(new AppWindowAnimParams(mAnimatingAppTokens.get(i).mAppAnimator)); + } + + layoutToAnim.mParamsModified = true; + scheduleAnimationLocked(); } } + void updateLayoutToAnimWallpaperTokens() { + synchronized(mLayoutToAnim) { + mLayoutToAnim.mWallpaperTokens = new ArrayList<WindowToken>(mWallpaperTokens); + mLayoutToAnim.mChanges |= LayoutToAnimatorParams.WALLPAPER_TOKENS_CHANGED; + } + } + + void setAnimDimParams(DimAnimator.Parameters params) { + synchronized (mLayoutToAnim) { + mLayoutToAnim.mDimParams = params; + scheduleAnimationLocked(); + } + } + + void startDimming(final WindowStateAnimator winAnimator, final float target, + final int width, final int height) { + setAnimDimParams(new DimAnimator.Parameters(winAnimator, width, height, target)); + } + + void stopDimming() { + setAnimDimParams(null); + } + + private boolean copyAnimToLayoutParamsLocked() { + boolean doRequest = false; + final WindowAnimator.AnimatorToLayoutParams animToLayout = mAnimator.mAnimToLayout; + synchronized (animToLayout) { + animToLayout.mUpdateQueued = false; + final int bulkUpdateParams = animToLayout.mBulkUpdateParams; + // TODO(cmautner): As the number of bits grows, use masks of bit groups to + // eliminate unnecessary tests. + if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) { + mInnerFields.mUpdateRotation = true; + doRequest = true; + } + if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) { + mInnerFields.mWallpaperMayChange = true; + doRequest = true; + } + if ((bulkUpdateParams & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) { + mInnerFields.mWallpaperForceHidingChanged = true; + doRequest = true; + } + if ((bulkUpdateParams & LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE) == 0) { + mInnerFields.mOrientationChangeComplete = false; + } else { + mInnerFields.mOrientationChangeComplete = true; + if (mWindowsFreezingScreen) { + doRequest = true; + } + } + if ((bulkUpdateParams & LayoutFields.SET_TURN_ON_SCREEN) != 0) { + mTurnOnScreen = true; + } + + mPendingLayoutChanges |= animToLayout.mPendingLayoutChanges; + if (mPendingLayoutChanges != 0) { + doRequest = true; + } + + mWindowDetachedWallpaper = animToLayout.mWindowDetachedWallpaper; + } + return doRequest; + } + boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation, boolean secure) { final Surface surface = winAnimator.mSurface; @@ -9088,10 +9296,11 @@ public class WindowManagerService extends IWindowManager.Stub // There was some problem... first, do a sanity check of the // window list to make sure we haven't left any dangling surfaces // around. - int N = mWindows.size(); + + AllWindowsIterator iterator = new AllWindowsIterator(); Slog.i(TAG, "Out of memory for surface! Looking for leaks..."); - for (int i=0; i<N; i++) { - WindowState ws = mWindows.get(i); + while (iterator.hasNext()) { + WindowState ws = iterator.next(); WindowStateAnimator wsa = ws.mWinAnimator; if (wsa.mSurface != null) { if (!mSessions.contains(wsa.mSession)) { @@ -9106,8 +9315,6 @@ public class WindowManagerService extends IWindowManager.Stub wsa.mSurface = null; ws.mHasSurface = false; mForceRemoves.add(ws); - i--; - N--; leakedSurface = true; } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) { Slog.w(TAG, "LEAKED SURFACE (app token hidden): " @@ -9126,8 +9333,13 @@ public class WindowManagerService extends IWindowManager.Stub if (!leakedSurface) { Slog.w(TAG, "No leaked surfaces; killing applicatons!"); SparseIntArray pidCandidates = new SparseIntArray(); - for (int i=0; i<N; i++) { - WindowStateAnimator wsa = mWindows.get(i).mWinAnimator; + iterator = new AllWindowsIterator(); + while (iterator.hasNext()) { + WindowState ws = iterator.next(); + if (mForceRemoves.contains(ws)) { + continue; + } + WindowStateAnimator wsa = ws.mWinAnimator; if (wsa.mSurface != null) { pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid); } @@ -9187,6 +9399,9 @@ public class WindowManagerService extends IWindowManager.Stub mLosingFocus.remove(newFocus); int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus); + // TODO(multidisplay): Focused windows on default display only. + final DisplayContent displayContent = getDefaultDisplayContent(); + final WindowState imWindow = mInputMethodWindow; if (newFocus != imWindow && oldFocus != imWindow) { if (moveInputMethodWindowsIfNeededLocked( @@ -9195,12 +9410,12 @@ public class WindowManagerService extends IWindowManager.Stub mLayoutNeeded = true; } if (mode == UPDATE_FOCUS_PLACING_SURFACES) { - performLayoutLockedInner(true /*initial*/, updateInputWindows); + performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows); focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) { // Client will do the layout, but we need to assign layers // for handleNewWindowLocked() below. - assignLayersLocked(); + assignLayersLocked(displayContent.getWindowList()); } } @@ -9208,7 +9423,7 @@ public class WindowManagerService extends IWindowManager.Stub // The change in focus caused us to need to do a layout. Okay. mLayoutNeeded = true; if (mode == UPDATE_FOCUS_PLACING_SURFACES) { - performLayoutLockedInner(true /*initial*/, updateInputWindows); + performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows); } } @@ -9232,12 +9447,19 @@ public class WindowManagerService extends IWindowManager.Stub WindowState result = null; WindowState win; + if (mAnimator.mUniverseBackground != null + && mAnimator.mUniverseBackground.mWin.canReceiveKeys()) { + return mAnimator.mUniverseBackground.mWin; + } + int nextAppIndex = mAppTokens.size()-1; WindowToken nextApp = nextAppIndex >= 0 ? mAppTokens.get(nextAppIndex) : null; - for (int i = mWindows.size() - 1; i >= 0; i--) { - win = mWindows.get(i); + // TODO(multidisplay): IMEs are only supported on the default display. + WindowList windows = getDefaultWindowList(); + for (int i = windows.size() - 1; i >= 0; i--) { + win = windows.get(i); if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Looking for focus: " + i @@ -9300,7 +9522,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mDisplay == null || !mPolicy.isScreenOnFully()) { + if (mDefaultDisplay == null || !mPolicy.isScreenOnFully()) { // No need to freeze the screen before the system is ready or if // the screen is off. return; @@ -9331,15 +9553,11 @@ public class WindowManagerService extends IWindowManager.Stub mAnimator.mScreenRotationAnimation = null; } + // TODO(multidisplay): rotation on main screen only. + DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo(); mAnimator.mScreenRotationAnimation = new ScreenRotationAnimation(mContext, - mFxSession, inTransaction, mCurDisplayWidth, mCurDisplayHeight, - mDisplay.getRotation()); - - if (!mAnimator.mScreenRotationAnimation.hasScreenshot()) { - Surface.freezeDisplay(0); - } - } else { - Surface.freezeDisplay(0); + mDefaultDisplay, mFxSession, inTransaction, displayInfo.logicalWidth, + displayInfo.logicalHeight, mDefaultDisplay.getRotation()); } } @@ -9363,13 +9581,16 @@ public class WindowManagerService extends IWindowManager.Stub } boolean updateRotation = false; - + if (CUSTOM_SCREEN_ROTATION && mAnimator.mScreenRotationAnimation != null && mAnimator.mScreenRotationAnimation.hasScreenshot()) { if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation"); + // TODO(multidisplay): rotation on main screen only. + DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo(); if (mAnimator.mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION, - mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) { - scheduleAnimationLocked(); + mTransitionAnimationScale, displayInfo.logicalWidth, + displayInfo.logicalHeight)) { + updateLayoutToAnimationLocked(); } else { mAnimator.mScreenRotationAnimation.kill(); mAnimator.mScreenRotationAnimation = null; @@ -9382,7 +9603,6 @@ public class WindowManagerService extends IWindowManager.Stub } updateRotation = true; } - Surface.unfreezeDisplay(0); mInputMonitor.thawInputDispatchingLw(); @@ -9448,7 +9668,8 @@ public class WindowManagerService extends IWindowManager.Stub if (line != null) { String[] toks = line.split("%"); if (toks != null && toks.length > 0) { - mWatermark = new Watermark(mRealDisplayMetrics, mFxSession, toks); + mWatermark = + new Watermark(mDefaultDisplay, mRealDisplayMetrics, mFxSession, toks); } } } catch (FileNotFoundException e) { @@ -9478,11 +9699,13 @@ public class WindowManagerService extends IWindowManager.Stub } } + // TOOD(multidisplay): StatusBar on multiple screens? void updateStatusBarVisibilityLocked(int visibility) { mInputManager.setSystemUiVisibility(visibility); - final int N = mWindows.size(); + final WindowList windows = getDefaultWindowList(); + final int N = windows.size(); for (int i = 0; i < N; i++) { - WindowState ws = mWindows.get(i); + WindowState ws = windows.get(i); try { int curValue = ws.mSystemUiVisibility; int diff = curValue ^ visibility; @@ -9549,6 +9772,8 @@ public class WindowManagerService extends IWindowManager.Stub // It is assumed that this method is called only by InputMethodManagerService. public void saveLastInputMethodWindowForTransition() { synchronized (mWindowMap) { + // TODO(multidisplay): Pass in the displayID. + DisplayContent displayContent = getDefaultDisplayContent(); if (mInputMethodWindow != null) { mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget); } @@ -9699,10 +9924,12 @@ public class WindowManagerService extends IWindowManager.Stub void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll, ArrayList<WindowState> windows) { - for (int i=mWindows.size()-1; i>=0; i--) { - WindowState w = mWindows.get(i); + int j = 0; + final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); + while (iterator.hasNext()) { + final WindowState w = iterator.next(); if (windows == null || windows.contains(w)) { - pw.print(" Window #"); pw.print(i); pw.print(' '); + pw.print(" Window #"); pw.print(j++); pw.print(' '); pw.print(w); pw.println(":"); w.dump(pw, " ", dumpAll || windows != null); } @@ -9810,27 +10037,11 @@ public class WindowManagerService extends IWindowManager.Stub } } pw.println(); - if (mDisplay != null) { - pw.print(" Display: init="); pw.print(mInitialDisplayWidth); pw.print("x"); - pw.print(mInitialDisplayHeight); - if (mInitialDisplayWidth != mBaseDisplayWidth - || mInitialDisplayHeight != mBaseDisplayHeight) { - pw.print(" base="); - pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight); - } - final int rawWidth = mDisplay.getRawWidth(); - final int rawHeight = mDisplay.getRawHeight(); - if (rawWidth != mCurDisplayWidth || rawHeight != mCurDisplayHeight) { - pw.print(" raw="); pw.print(rawWidth); pw.print("x"); pw.print(rawHeight); - } - pw.print(" cur="); - pw.print(mCurDisplayWidth); pw.print("x"); pw.print(mCurDisplayHeight); - pw.print(" app="); - pw.print(mAppDisplayWidth); pw.print("x"); pw.print(mAppDisplayHeight); - pw.print(" rng="); pw.print(mSmallestDisplayWidth); - pw.print("x"); pw.print(mSmallestDisplayHeight); - pw.print("-"); pw.print(mLargestDisplayWidth); - pw.print("x"); pw.println(mLargestDisplayHeight); + if (mDefaultDisplay != null) { + DisplayContentsIterator dCIterator = new DisplayContentsIterator(); + while (dCIterator.hasNext()) { + dCIterator.next().dump(pw); + } } else { pw.println(" NO DISPLAY"); } @@ -9847,7 +10058,8 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); if (dumpAll) { pw.print(" mSystemDecorRect="); pw.print(mSystemDecorRect.toShortString()); - pw.print(" mSystemDecorLayer="); pw.println(mSystemDecorLayer); + pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer); + pw.print(" mScreenRecr="); pw.println(mScreenRect.toShortString()); if (mLastStatusBarVisibility != 0) { pw.print(" mLastStatusBarVisibility=0x"); pw.println(Integer.toHexString(mLastStatusBarVisibility)); @@ -9916,15 +10128,15 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mNextAppTransitionStartHeight="); pw.println(mNextAppTransitionStartHeight); break; - case ActivityOptions.ANIM_THUMBNAIL: - case ActivityOptions.ANIM_THUMBNAIL_DELAYED: + case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP: + case ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN: pw.print(" mNextAppTransitionThumbnail="); pw.print(mNextAppTransitionThumbnail); pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX); pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY); - pw.print(" mNextAppTransitionDelayed="); pw.println(mNextAppTransitionDelayed); + pw.print(" mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp); break; } if (mNextAppTransitionCallback != null) { @@ -9943,8 +10155,9 @@ public class WindowManagerService extends IWindowManager.Stub ArrayList<WindowState> windows = new ArrayList<WindowState>(); if ("visible".equals(name)) { synchronized(mWindowMap) { - for (int i=mWindows.size()-1; i>=0; i--) { - WindowState w = mWindows.get(i); + final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); + while (iterator.hasNext()) { + final WindowState w = iterator.next(); if (w.mWinAnimator.mSurfaceShown) { windows.add(w); } @@ -9959,8 +10172,9 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RuntimeException e) { } synchronized(mWindowMap) { - for (int i=mWindows.size()-1; i>=0; i--) { - WindowState w = mWindows.get(i); + final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); + while (iterator.hasNext()) { + final WindowState w = iterator.next(); if (name != null) { if (w.mAttrs.getTitle().toString().contains(name)) { windows.add(w); @@ -10133,7 +10347,6 @@ public class WindowManagerService extends IWindowManager.Stub // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection). public void monitor() { synchronized (mWindowMap) { } - synchronized (mKeyguardTokenWatcher) { } } public interface OnHardKeyboardStatusChangeListener { @@ -10147,8 +10360,108 @@ public class WindowManagerService extends IWindowManager.Stub } } - void bulkSetParameters(final int bulkUpdateParams, int pendingLayoutChanges) { - mH.sendMessage(mH.obtainMessage(H.BULK_UPDATE_PARAMETERS, bulkUpdateParams, - pendingLayoutChanges)); + public DisplayContent getDisplayContent(final int displayId) { + DisplayContent displayContent = mDisplayContents.get(displayId); + if (displayContent == null) { + displayContent = new DisplayContent(mDisplayManager.getRealDisplay(displayId)); + mDisplayContents.put(displayId, displayContent); + } + return displayContent; + } + + class DisplayContentsIterator implements Iterator<DisplayContent> { + private int cur; + + @Override + public boolean hasNext() { + return cur < mDisplayContents.size(); + } + + @Override + public DisplayContent next() { + if (hasNext()) { + return mDisplayContents.valueAt(cur++); + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new IllegalArgumentException("AllDisplayContentIterator.remove not implemented"); + } + } + + boolean REVERSE_ITERATOR = true; + class AllWindowsIterator implements Iterator<WindowState> { + private DisplayContent mDisplayContent; + private DisplayContentsIterator mDisplayContentsIterator; + private WindowList mWindowList; + private int mWindowListIndex; + private boolean mReverse; + + AllWindowsIterator() { + mDisplayContentsIterator = new DisplayContentsIterator(); + mDisplayContent = mDisplayContentsIterator.next(); + mWindowList = mDisplayContent.getWindowList(); + } + + AllWindowsIterator(boolean reverse) { + this(); + mReverse = reverse; + mWindowListIndex = reverse ? mWindowList.size() - 1 : 0; + } + + @Override + public boolean hasNext() { + if (mReverse) { + return mWindowListIndex >= 0; + } + return mWindowListIndex < mWindowList.size(); + } + + @Override + public WindowState next() { + if (hasNext()) { + WindowState win = mWindowList.get(mWindowListIndex); + if (mReverse) { + mWindowListIndex--; + if (mWindowListIndex < 0 && mDisplayContentsIterator.hasNext()) { + mDisplayContent = mDisplayContentsIterator.next(); + mWindowList = mDisplayContent.getWindowList(); + mWindowListIndex = mWindowList.size() - 1; + } + } else { + mWindowListIndex++; + if (mWindowListIndex >= mWindowList.size() + && mDisplayContentsIterator.hasNext()) { + mDisplayContent = mDisplayContentsIterator.next(); + mWindowList = mDisplayContent.getWindowList(); + mWindowListIndex = 0; + } + } + return win; + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new IllegalArgumentException("AllWindowsIterator.remove not implemented"); + } } + + public DisplayContent getDefaultDisplayContent() { + final int displayId = mDefaultDisplay == null + ? Display.DEFAULT_DISPLAY : mDefaultDisplay.getDisplayId(); + return getDisplayContent(displayId); + } + + public WindowList getDefaultWindowList() { + return getDefaultDisplayContent().getWindowList(); + } + + public DisplayInfo getDefaultDisplayInfo() { + return getDefaultDisplayContent().getDisplayInfo(); + } + } diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index a00e8a5..a52e1d7 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -22,6 +22,9 @@ 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; @@ -33,8 +36,11 @@ 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; +import android.view.DisplayInfo; import android.view.Gravity; import android.view.IApplicationToken; import android.view.IWindow; @@ -47,6 +53,9 @@ import android.view.WindowManagerPolicy; import java.io.PrintWriter; import java.util.ArrayList; +class WindowList extends ArrayList<WindowState> { +} + /** * A window in the window manager. */ @@ -251,15 +260,22 @@ final class WindowState implements WindowManagerPolicy.WindowState { boolean mHasSurface = false; + DisplayContent mDisplayContent; + + // UserId and appId of the owner. Don't display windows of non-current user. + final int mOwnerUid; + WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState attachedWindow, int seq, WindowManager.LayoutParams a, - int viewVisibility) { + int viewVisibility, final DisplayContent displayContent) { mService = service; mSession = s; mClient = c; mToken = token; + mOwnerUid = s.mUid; mAttrs.copyFrom(a); mViewVisibility = viewVisibility; + mDisplayContent = displayContent; mPolicy = mService.mPolicy; mContext = mService.mContext; DeathRecipient deathRecipient = new DeathRecipient(); @@ -317,9 +333,6 @@ final class WindowState implements WindowManagerPolicy.WindowState { mIsFloatingLayer = mIsImWindow || mIsWallpaper; } - mWinAnimator = new WindowStateAnimator(service, this, mAttachedWindow); - mWinAnimator.mAlpha = a.alpha; - WindowState appWin = this; while (appWin.mAttachedWindow != null) { appWin = appWin.mAttachedWindow; @@ -335,6 +348,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { mRootToken = appToken; mAppToken = appToken.appWindowToken; + mWinAnimator = new WindowStateAnimator(this); + mWinAnimator.mAlpha = a.alpha; + mRequestedWidth = 0; mRequestedHeight = 0; mLastRequestedWidth = 0; @@ -343,7 +359,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { mYOffset = 0; mLayer = 0; mInputWindowHandle = new InputWindowHandle( - mAppToken != null ? mAppToken.mInputApplicationHandle : null, this); + mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, + displayContent.getDisplayId()); } void attach() { @@ -479,8 +496,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { } if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { - mService.updateWallpaperOffsetLocked(this, - mService.mAppDisplayWidth, mService.mAppDisplayHeight, false); + final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); + mService.updateWallpaperOffsetLocked(this, displayInfo.appWidth, displayInfo.appHeight, + false); } if (WindowManagerService.localLOGV) { @@ -544,6 +562,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) { int index = -1; WindowState ws = this; + WindowList windows = getWindowList(); while (true) { if ((ws.mAttrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY) != 0) { @@ -558,13 +577,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { // look behind it. // First, we may need to determine the starting position. if (index < 0) { - index = mService.mWindows.indexOf(ws); + index = windows.indexOf(ws); } index--; if (index < 0) { return false; } - ws = mService.mWindows.get(index); + ws = windows.get(index); } } @@ -884,6 +903,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { } boolean showLw(boolean doAnimation, boolean requestAnim) { + if (isOtherUsersAppWindow()) { + Slog.w(TAG, "Current user " + mService.mCurrentUserId + " trying to display " + + this + ", type " + mAttrs.type + ", belonging to " + mOwnerUid); + return false; + } if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { // Already showing. return false; @@ -907,7 +931,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_ENTER, true); } if (requestAnim) { - mService.scheduleAnimationLocked(); + mService.updateLayoutToAnimationLocked(); } return true; } @@ -950,7 +974,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } if (requestAnim) { - mService.scheduleAnimationLocked(); + mService.updateLayoutToAnimationLocked(); } return true; } @@ -960,6 +984,17 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mClient.asBinder().isBinderAlive(); } + 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; + } + return false; + } + private static void applyInsets(Region outRegion, Rect frame, Rect inset) { outRegion.set( frame.left + inset.left, frame.top + inset.top, @@ -988,8 +1023,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + WindowList getWindowList() { + return mDisplayContent.getWindowList(); + } + void dump(PrintWriter pw, String prefix, boolean dumpAll) { - pw.print(prefix); pw.print("mSession="); pw.print(mSession); + 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("mAttrs="); pw.println(mAttrs); pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); @@ -1060,6 +1100,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (mTouchableInsets != 0 || mGivenInsetsPending) { pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets); pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending); + Region region = new Region(); + getTouchableRegion(region); + pw.print(prefix); pw.print("touchable region="); pw.println(region); } pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration); } diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index 03e52fe..982f60d 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -3,9 +3,8 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static com.android.server.wm.WindowManagerService.LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE; +import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN; import android.content.Context; @@ -17,6 +16,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Debug; import android.util.Slog; +import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceSession; import android.view.WindowManager; @@ -31,6 +31,9 @@ import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; import java.util.ArrayList; +class WinAnimatorList extends ArrayList<WindowStateAnimator> { +} + /** * Keep track of animations and surface operations for a single WindowState. **/ @@ -48,13 +51,20 @@ class WindowStateAnimator { static final String TAG = "WindowStateAnimator"; + // Unchanging local convenience fields. final WindowManagerService mService; final WindowState mWin; - final WindowState mAttachedWindow; + final WindowStateAnimator mAttachedWinAnimator; final WindowAnimator mAnimator; + final AppWindowAnimator mAppAnimator; final Session mSession; final WindowManagerPolicy mPolicy; final Context mContext; + final boolean mIsWallpaper; + + // If this is a universe background window, this is the transformation + // it is applying to the rest of the universe. + final Transformation mUniverseTransform = new Transformation(); // Currently running animation. boolean mAnimating; @@ -138,19 +148,28 @@ class WindowStateAnimator { int mAttrFlags; int mAttrType; - public WindowStateAnimator(final WindowManagerService service, final WindowState win, - final WindowState attachedWindow) { + final int mLayerStack; + + public WindowStateAnimator(final WindowState win) { + final WindowManagerService service = win.mService; + mService = service; + mAnimator = service.mAnimator; + mPolicy = service.mPolicy; + mContext = service.mContext; + final DisplayInfo displayInfo = win.mDisplayContent.getDisplayInfo(); + mAnimDw = displayInfo.appWidth; + mAnimDh = displayInfo.appHeight; + mWin = win; - mAttachedWindow = attachedWindow; - mAnimator = mService.mAnimator; + mAttachedWinAnimator = win.mAttachedWindow == null + ? null : win.mAttachedWindow.mWinAnimator; + mAppAnimator = win.mAppToken == null ? null : win.mAppToken.mAppAnimator; mSession = win.mSession; - mPolicy = mService.mPolicy; - mContext = mService.mContext; mAttrFlags = win.mAttrs.flags; mAttrType = win.mAttrs.type; - mAnimDw = service.mAppDisplayWidth; - mAnimDh = service.mAppDisplayHeight; + mIsWallpaper = win.mIsWallpaper; + mLayerStack = win.mDisplayContent.getDisplay().getLayerStack(); } public void setAnimation(Animation anim) { @@ -177,20 +196,17 @@ class WindowStateAnimator { /** Is the window or its container currently animating? */ boolean isAnimating() { - final WindowState attached = mAttachedWindow; - final AppWindowToken atoken = mWin.mAppToken; return mAnimation != null - || (attached != null && attached.mWinAnimator.mAnimation != null) - || (atoken != null && - (atoken.mAppAnimator.animation != null - || atoken.inPendingTransaction)); + || (mAttachedWinAnimator != null && mAttachedWinAnimator.mAnimation != null) + || (mAppAnimator != null && + (mAppAnimator.animation != null + || mAppAnimator.mAppToken.inPendingTransaction)); } /** Is the window animating the DummyAnimation? */ boolean isDummyAnimation() { - final AppWindowToken atoken = mWin.mAppToken; - return atoken != null - && atoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation; + return mAppAnimator != null + && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation; } /** Is this window currently animating? */ @@ -213,7 +229,7 @@ class WindowStateAnimator { } mTransformation.clear(); final boolean more = mAnimation.getTransformation(currentTime, mTransformation); - if (DEBUG_ANIM) Slog.v( + if (false && DEBUG_ANIM) Slog.v( TAG, "Stepped animation in " + this + ": more=" + more + ", xform=" + mTransformation); return more; @@ -240,8 +256,9 @@ class WindowStateAnimator { " scale=" + mService.mWindowAnimationScale); mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDw, mAnimDh); - mAnimDw = mService.mAppDisplayWidth; - mAnimDh = mService.mAppDisplayHeight; + final DisplayInfo displayInfo = mWin.mDisplayContent.getDisplayInfo(); + mAnimDw = displayInfo.appWidth; + mAnimDh = displayInfo.appHeight; mAnimation.setStartTime(currentTime); mLocalAnimating = true; mAnimating = true; @@ -257,8 +274,8 @@ class WindowStateAnimator { //WindowManagerService.this.dump(); } mHasLocalTransformation = false; - if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null - && mWin.mAppToken.mAppAnimator.animation != null) { + if ((!mLocalAnimating || mAnimationIsEntrance) && mAppAnimator != null + && mAppAnimator.animation != null) { // When our app token is animating, we kind-of pretend like // we are as well. Note the mLocalAnimating mAnimationIsEntrance // part of this check means that we will only do this if @@ -305,7 +322,7 @@ class WindowStateAnimator { mAnimLayer = mWin.mLayer; if (mWin.mIsImWindow) { mAnimLayer += mService.mInputMethodAnimLayerAdjustment; - } else if (mWin.mIsWallpaper) { + } else if (mIsWallpaper) { mAnimLayer += mService.mWallpaperAnimLayerAdjustment; } if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this @@ -460,22 +477,14 @@ class WindowStateAnimator { private final Point mSize = new Point(); private final Rect mWindowCrop = new Rect(); private boolean mShown = false; - private String mName = "Not named"; - - public SurfaceTrace(SurfaceSession s, - int pid, int display, int w, int h, int format, int flags) throws - OutOfResourcesException { - super(s, pid, display, w, h, format, flags); - mSize.set(w, h); - Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by " - + Debug.getCallers(3)); - } + private int mLayerStack; + private String mName; public SurfaceTrace(SurfaceSession s, - int pid, String name, int display, int w, int h, int format, int flags) + int pid, String name, int layerStack, int w, int h, int format, int flags) throws OutOfResourcesException { - super(s, pid, name, display, w, h, format, flags); - mName = name; + super(s, pid, name, layerStack, w, h, format, flags); + mName = name != null ? name : "Not named"; mSize.set(w, h); Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by " + Debug.getCallers(3)); @@ -534,6 +543,13 @@ class WindowStateAnimator { } @Override + public void setLayerStack(int layerStack) { + super.setLayerStack(layerStack); + mLayerStack = layerStack; + Slog.v(SURFACE_TAG, "setLayerStack: " + this + ". Called by " + Debug.getCallers(3)); + } + + @Override public void hide() { super.hide(); mShown = false; @@ -574,7 +590,7 @@ class WindowStateAnimator { @Override public String toString() { return "Surface " + Integer.toHexString(System.identityHashCode(this)) + " " - + mName + ": shown=" + mShown + " layer=" + mLayer + + mName + " (" + mLayerStack + "): shown=" + mShown + " layer=" + mLayer + " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y + " " + mSize.x + "x" + mSize.y + " crop=" + mWindowCrop.toShortString(); @@ -638,12 +654,12 @@ class WindowStateAnimator { mSurface = new SurfaceTrace( mSession.mSurfaceSession, mSession.mPid, attrs.getTitle().toString(), - 0, w, h, format, flags); + mLayerStack, w, h, format, flags); } else { mSurface = new Surface( mSession.mSurfaceSession, mSession.mPid, attrs.getTitle().toString(), - 0, w, h, format, flags); + mLayerStack, w, h, format, flags); } mWin.mHasSurface = true; if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG, @@ -689,10 +705,6 @@ class WindowStateAnimator { mSurface.setAlpha(0); mSurfaceShown = false; mSurface.hide(); - if ((mWin.mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) { - if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "DITHER", null); - mSurface.setFlags(Surface.SURFACE_DITHER, Surface.SURFACE_DITHER); - } } catch (RuntimeException e) { Slog.w(TAG, "Error creating surface in " + w, e); mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true); @@ -799,31 +811,28 @@ class WindowStateAnimator { void computeShownFrameLocked() { final boolean selfTransformation = mHasLocalTransformation; Transformation attachedTransformation = - (mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation) - ? mAttachedWindow.mWinAnimator.mTransformation : null; - final AppWindowAnimator appAnimator = - mWin.mAppToken == null ? null : mWin.mAppToken.mAppAnimator; - Transformation appTransformation = (appAnimator != null && appAnimator.hasTransformation) - ? appAnimator.transformation : null; + (mAttachedWinAnimator != null && mAttachedWinAnimator.mHasLocalTransformation) + ? mAttachedWinAnimator.mTransformation : null; + Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation) + ? mAppAnimator.transformation : null; // Wallpapers are animated based on the "real" window they // are currently targeting. - if (mWin.mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null - && mService.mWallpaperTarget != null) { - if (mService.mWallpaperTarget.mWinAnimator.mHasLocalTransformation && - mService.mWallpaperTarget.mWinAnimator.mAnimation != null && - !mService.mWallpaperTarget.mWinAnimator.mAnimation.getDetachWallpaper()) { - attachedTransformation = mService.mWallpaperTarget.mWinAnimator.mTransformation; + if (mIsWallpaper && mAnimator.mLowerWallpaperTarget == null + && mAnimator.mWallpaperTarget != null) { + final WindowStateAnimator wallpaperAnimator = mAnimator.mWallpaperTarget.mWinAnimator; + if (wallpaperAnimator.mHasLocalTransformation && + wallpaperAnimator.mAnimation != null && + !wallpaperAnimator.mAnimation.getDetachWallpaper()) { + attachedTransformation = wallpaperAnimator.mTransformation; if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) { Slog.v(TAG, "WP target attached xform: " + attachedTransformation); } } - final AppWindowAnimator wpAppAnimator = mService.mWallpaperTarget.mAppToken == null - ? null : mService.mWallpaperTarget.mAppToken.mAppAnimator; - if (wpAppAnimator != null && - wpAppAnimator.hasTransformation && - wpAppAnimator.animation != null && - !wpAppAnimator.animation.getDetachWallpaper()) { + final AppWindowAnimator wpAppAnimator = mAnimator.mWpAppAnimator; + if (wpAppAnimator != null && wpAppAnimator.hasTransformation + && wpAppAnimator.animation != null + && !wpAppAnimator.animation.getDetachWallpaper()) { appTransformation = wpAppAnimator.transformation; if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) { Slog.v(TAG, "WP target app xform: " + appTransformation); @@ -870,6 +879,9 @@ class WindowStateAnimator { if (appTransformation != null) { tmpMatrix.postConcat(appTransformation.getMatrix()); } + if (mAnimator.mUniverseBackground != null) { + tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix()); + } if (screenAnimation) { tmpMatrix.postConcat( mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getMatrix()); @@ -913,6 +925,9 @@ class WindowStateAnimator { if (appTransformation != null) { mShownAlpha *= appTransformation.getAlpha(); } + if (mAnimator.mUniverseBackground != null) { + mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha(); + } if (screenAnimation) { mShownAlpha *= mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getAlpha(); @@ -921,7 +936,7 @@ class WindowStateAnimator { //Slog.i(TAG, "Not applying alpha transform"); } - if (WindowManagerService.localLOGV && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v( + if ((DEBUG_SURFACE_TRACE || WindowManagerService.localLOGV) && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v( TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha + " self=" + (selfTransformation ? mTransformation.getAlpha() : "null") + @@ -930,7 +945,7 @@ class WindowStateAnimator { " screen=" + (screenAnimation ? mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getAlpha() : "null")); return; - } else if (mWin.mIsWallpaper && + } else if (mIsWallpaper && (mAnimator.mPendingActions & WindowAnimator.WALLPAPER_ACTION_PENDING) != 0) { return; } @@ -938,16 +953,64 @@ class WindowStateAnimator { if (WindowManagerService.localLOGV) Slog.v( TAG, "computeShownFrameLocked: " + this + " not attached, mAlpha=" + mAlpha); - mWin.mShownFrame.set(mWin.mFrame); - if (mWin.mXOffset != 0 || mWin.mYOffset != 0) { - mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset); - } - mShownAlpha = mAlpha; - mHaveMatrix = false; - mDsDx = mWin.mGlobalScale; - mDtDx = 0; - mDsDy = 0; - mDtDy = mWin.mGlobalScale; + if (mAnimator.mUniverseBackground != null && + mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND + && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer) { + final Rect frame = mWin.mFrame; + final float tmpFloats[] = mService.mTmpFloats; + final Matrix tmpMatrix = mWin.mTmpMatrix; + tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale); + tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset); + tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix()); + tmpMatrix.getValues(tmpFloats); + mHaveMatrix = true; + mDsDx = tmpFloats[Matrix.MSCALE_X]; + mDtDx = tmpFloats[Matrix.MSKEW_Y]; + mDsDy = tmpFloats[Matrix.MSKEW_X]; + mDtDy = tmpFloats[Matrix.MSCALE_Y]; + float x = tmpFloats[Matrix.MTRANS_X]; + float y = tmpFloats[Matrix.MTRANS_Y]; + int w = frame.width(); + int h = frame.height(); + mWin.mShownFrame.set(x, y, x+w, y+h); + mShownAlpha = mAlpha * mAnimator.mUniverseBackground.mUniverseTransform.getAlpha(); + } else { + mWin.mShownFrame.set(mWin.mFrame); + if (mWin.mXOffset != 0 || mWin.mYOffset != 0) { + mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset); + } + mShownAlpha = mAlpha; + mHaveMatrix = false; + mDsDx = mWin.mGlobalScale; + mDtDx = 0; + mDsDy = 0; + mDtDy = mWin.mGlobalScale; + } + } + + void applyDecorRect(final Rect decorRect) { + final WindowState w = mWin; + // Compute the offset of the window in relation to the decor rect. + final int offX = w.mXOffset + w.mFrame.left; + final int offY = w.mYOffset + w.mFrame.top; + // Initialize the decor rect to the entire frame. + w.mSystemDecorRect.set(0, 0, w.mFrame.width(), w.mFrame.height()); + // Intersect with the decor rect, offsetted by window position. + w.mSystemDecorRect.intersect(decorRect.left-offX, decorRect.top-offY, + decorRect.right-offX, decorRect.bottom-offY); + // If size compatibility is being applied to the window, the + // surface is scaled relative to the screen. Also apply this + // scaling to the crop rect. We aren't using the standard rect + // scale function because we want to round things to make the crop + // always round to a larger rect to ensure we don't crop too + // much and hide part of the window that should be seen. + if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) { + final float scale = w.mInvGlobalScale; + w.mSystemDecorRect.left = (int) (w.mSystemDecorRect.left * scale - 0.5f); + w.mSystemDecorRect.top = (int) (w.mSystemDecorRect.top * scale - 0.5f); + w.mSystemDecorRect.right = (int) ((w.mSystemDecorRect.right+1) * scale - 0.5f); + w.mSystemDecorRect.bottom = (int) ((w.mSystemDecorRect.bottom+1) * scale - 0.5f); + } } void updateSurfaceWindowCrop(final boolean recoveringMemory) { @@ -960,31 +1023,21 @@ class WindowStateAnimator { w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight); } else if (w.mLayer >= mService.mSystemDecorLayer) { // Above the decor layer is easy, just use the entire window. + // Unless we have a universe background... in which case all the + // windows need to be cropped by the screen, so they don't cover + // the universe background. + if (mAnimator.mUniverseBackground == null) { + w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), + w.mCompatFrame.height()); + } else { + applyDecorRect(mService.mScreenRect); + } + } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) { + // The universe background isn't cropped. w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height()); } else { - final Rect decorRect = mService.mSystemDecorRect; - // Compute the offset of the window in relation to the decor rect. - final int offX = w.mXOffset + w.mFrame.left; - final int offY = w.mYOffset + w.mFrame.top; - // Initialize the decor rect to the entire frame. - w.mSystemDecorRect.set(0, 0, w.mFrame.width(), w.mFrame.height()); - // Intersect with the decor rect, offsetted by window position. - w.mSystemDecorRect.intersect(decorRect.left-offX, decorRect.top-offY, - decorRect.right-offX, decorRect.bottom-offY); - // If size compatibility is being applied to the window, the - // surface is scaled relative to the screen. Also apply this - // scaling to the crop rect. We aren't using the standard rect - // scale function because we want to round things to make the crop - // always round to a larger rect to ensure we don't crop too - // much and hide part of the window that should be seen. - if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) { - final float scale = w.mInvGlobalScale; - w.mSystemDecorRect.left = (int) (w.mSystemDecorRect.left * scale - 0.5f); - w.mSystemDecorRect.top = (int) (w.mSystemDecorRect.top * scale - 0.5f); - w.mSystemDecorRect.right = (int) ((w.mSystemDecorRect.right+1) * scale - 0.5f); - w.mSystemDecorRect.bottom = (int) ((w.mSystemDecorRect.bottom+1) * scale - 0.5f); - } + applyDecorRect(mService.mSystemDecorRect); } if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) { @@ -1056,8 +1109,9 @@ class WindowStateAnimator { mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) { - mAnimator.startDimming(this, w.mExiting ? 0 : w.mAttrs.dimAmount, - mService.mAppDisplayWidth, mService.mAppDisplayHeight); + final DisplayInfo displayInfo = mWin.mDisplayContent.getDisplayInfo(); + mService.startDimming(this, w.mExiting ? 0 : w.mAttrs.dimAmount, + displayInfo.appWidth, displayInfo.appHeight); } } catch (RuntimeException e) { // If something goes wrong with the surface (such @@ -1092,7 +1146,7 @@ class WindowStateAnimator { setSurfaceBoundaries(recoveringMemory); - if (mWin.mIsWallpaper && !mWin.mWallpaperVisible) { + if (mIsWallpaper && !mWin.mWallpaperVisible) { // Wallpaper is no longer visible and there is no wp target => hide it. hide(); } else if (w.mAttachedHidden || !w.isReadyForDisplay()) { @@ -1151,7 +1205,7 @@ class WindowStateAnimator { + " during relayout"); if (showSurfaceRobustlyLocked()) { mLastHidden = false; - if (w.mIsWallpaper) { + if (mIsWallpaper) { mService.dispatchWallpaperVisibility(w, true); } } else { @@ -1169,8 +1223,8 @@ class WindowStateAnimator { } } } else { - if (DEBUG_ANIM) { - // Slog.v(TAG, "prepareSurface: No changes in animation for " + mWin); + if (DEBUG_ANIM && isAnimating()) { + Slog.v(TAG, "prepareSurface: No changes in animation for " + this); } displayed = true; } @@ -1178,7 +1232,7 @@ class WindowStateAnimator { if (displayed) { if (w.mOrientationChanging) { if (!w.isDrawnLw()) { - mAnimator.mBulkUpdateParams |= CLEAR_ORIENTATION_CHANGE_COMPLETE; + mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation continue waiting for draw in " + w); } else { @@ -1239,6 +1293,11 @@ 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 " + + this + ", type " + mWin.mAttrs.type + ", belonging to " + mWin.mOwnerUid); + return false; + } if (DEBUG_VISIBILITY || (DEBUG_STARTING_WINDOW && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING)) { RuntimeException e = null; @@ -1258,7 +1317,7 @@ class WindowStateAnimator { + (mWin.mAppToken != null ? mWin.mAppToken.hidden : false) + " animating=" + mAnimating + " tok animating=" - + (mWin.mAppToken != null ? mWin.mAppToken.mAppAnimator.animating : false), e); + + (mAppAnimator != null ? mAppAnimator.animating : false), e); } if (mDrawState == READY_TO_SHOW && mWin.isReadyForDisplayIgnoringKeyguard()) { if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) @@ -1274,7 +1333,7 @@ class WindowStateAnimator { + (mWin.mAppToken != null ? mWin.mAppToken.hidden : false) + " animating=" + mAnimating + " tok animating=" - + (mWin.mAppToken != null ? mWin.mAppToken.mAppAnimator.animating : false)); + + (mAppAnimator != null ? mAppAnimator.animating : false)); } mService.enableScreenIfNeededLocked(); @@ -1286,7 +1345,7 @@ class WindowStateAnimator { if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + this); mDrawState = HAS_DRAWN; - mService.scheduleAnimationLocked(); + mService.updateLayoutToAnimationLocked(); int i = mWin.mChildWindows.size(); while (i > 0) { @@ -1483,6 +1542,11 @@ class WindowStateAnimator { pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized); pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred); } + if (mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) { + pw.print(prefix); pw.print("mUniverseTransform="); + mUniverseTransform.printShortString(pw); + pw.println(); + } if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) { pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha); pw.print(" mAlpha="); pw.print(mAlpha); |
