summaryrefslogtreecommitdiffstats
path: root/services/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java')
-rw-r--r--services/java/Android.mk3
-rw-r--r--services/java/com/android/server/AlarmManagerService.java89
-rw-r--r--services/java/com/android/server/AppWidgetService.java112
-rw-r--r--services/java/com/android/server/AppWidgetServiceImpl.java219
-rw-r--r--services/java/com/android/server/BackupManagerService.java45
-rw-r--r--services/java/com/android/server/BatteryService.java55
-rwxr-xr-xservices/java/com/android/server/BluetoothManagerService.java773
-rw-r--r--services/java/com/android/server/ClipboardService.java26
-rw-r--r--services/java/com/android/server/CommonTimeManagementService.java2
-rw-r--r--services/java/com/android/server/ConnectivityService.java620
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java76
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java118
-rw-r--r--services/java/com/android/server/DockObserver.java232
-rw-r--r--services/java/com/android/server/DropBoxManagerService.java4
-rw-r--r--services/java/com/android/server/EventLogTags.logtags15
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java58
-rw-r--r--services/java/com/android/server/IntentResolver.java290
-rw-r--r--services/java/com/android/server/IntentResolverOld.java638
-rw-r--r--services/java/com/android/server/LightsService.java30
-rw-r--r--services/java/com/android/server/LocationManagerService.java2366
-rw-r--r--services/java/com/android/server/MountService.java583
-rw-r--r--services/java/com/android/server/NativeDaemonConnector.java129
-rw-r--r--services/java/com/android/server/NetworkManagementService.java219
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java114
-rw-r--r--services/java/com/android/server/NsdService.java3
-rw-r--r--services/java/com/android/server/PowerManagerService.java3405
-rw-r--r--services/java/com/android/server/ServiceWatcher.java274
-rw-r--r--services/java/com/android/server/ShutdownActivity.java3
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java1
-rw-r--r--services/java/com/android/server/SystemBackupAgent.java8
-rw-r--r--services/java/com/android/server/SystemServer.java162
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java21
-rw-r--r--services/java/com/android/server/ThrottleService.java12
-rw-r--r--services/java/com/android/server/TwilightService.java572
-rw-r--r--services/java/com/android/server/UiModeManagerService.java354
-rw-r--r--services/java/com/android/server/UpdateLockService.java3
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java222
-rw-r--r--services/java/com/android/server/Watchdog.java3
-rw-r--r--services/java/com/android/server/WifiService.java188
-rw-r--r--services/java/com/android/server/WiredAccessoryObserver.java305
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityInputFilter.java181
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java331
-rw-r--r--services/java/com/android/server/accessibility/EventStreamTransformation.java90
-rw-r--r--services/java/com/android/server/accessibility/GestureUtils.java102
-rw-r--r--services/java/com/android/server/accessibility/ScreenMagnifier.java1798
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java264
-rw-r--r--services/java/com/android/server/am/ActiveServices.java2158
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java4084
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java18
-rwxr-xr-xservices/java/com/android/server/am/ActivityStack.java208
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java20
-rw-r--r--services/java/com/android/server/am/BroadcastFilter.java6
-rw-r--r--services/java/com/android/server/am/BroadcastQueue.java80
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java22
-rw-r--r--services/java/com/android/server/am/CompatModePackages.java3
-rw-r--r--services/java/com/android/server/am/ConnectionRecord.java28
-rw-r--r--services/java/com/android/server/am/ContentProviderRecord.java10
-rw-r--r--services/java/com/android/server/am/IntentBindRecord.java18
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java27
-rw-r--r--services/java/com/android/server/am/ProcessList.java3
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java15
-rw-r--r--services/java/com/android/server/am/ProviderMap.java204
-rw-r--r--services/java/com/android/server/am/ReceiverList.java11
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java13
-rw-r--r--services/java/com/android/server/am/TaskRecord.java4
-rw-r--r--services/java/com/android/server/am/UsageStatsService.java2
-rw-r--r--services/java/com/android/server/am/UserStartedState.java43
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java38
-rw-r--r--services/java/com/android/server/connectivity/Vpn.java451
-rw-r--r--services/java/com/android/server/display/DisplayAdapter.java128
-rw-r--r--services/java/com/android/server/display/DisplayDevice.java200
-rw-r--r--services/java/com/android/server/display/DisplayDeviceInfo.java190
-rw-r--r--services/java/com/android/server/display/DisplayManagerService.java888
-rw-r--r--services/java/com/android/server/display/DisplayViewport.java75
-rw-r--r--services/java/com/android/server/display/HeadlessDisplayAdapter.java69
-rw-r--r--services/java/com/android/server/display/LocalDisplayAdapter.java151
-rw-r--r--services/java/com/android/server/display/LogicalDisplay.java308
-rw-r--r--services/java/com/android/server/display/OverlayDisplayAdapter.java338
-rw-r--r--services/java/com/android/server/display/OverlayDisplayWindow.java367
-rw-r--r--services/java/com/android/server/display/WifiDisplayAdapter.java346
-rw-r--r--services/java/com/android/server/display/WifiDisplayController.java820
-rw-r--r--services/java/com/android/server/input/InputFilter.java258
-rw-r--r--services/java/com/android/server/input/InputManagerService.java109
-rw-r--r--services/java/com/android/server/input/InputWindowHandle.java6
-rw-r--r--services/java/com/android/server/input/PersistentDataStore.java2
-rwxr-xr-xservices/java/com/android/server/location/ComprehensiveCountryDetector.java4
-rw-r--r--services/java/com/android/server/location/GeocoderProxy.java92
-rw-r--r--services/java/com/android/server/location/GeofenceManager.java258
-rw-r--r--services/java/com/android/server/location/GeofenceState.java104
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java715
-rwxr-xr-xservices/java/com/android/server/location/LocationBasedCountryDetector.java4
-rw-r--r--services/java/com/android/server/location/LocationBlacklist.java135
-rw-r--r--services/java/com/android/server/location/LocationFudger.java397
-rw-r--r--services/java/com/android/server/location/LocationProviderInterface.java53
-rw-r--r--services/java/com/android/server/location/LocationProviderProxy.java570
-rw-r--r--services/java/com/android/server/location/MockProvider.java156
-rw-r--r--services/java/com/android/server/location/PassiveProvider.java94
-rw-r--r--services/java/com/android/server/net/BaseNetworkObserver.java (renamed from services/java/com/android/server/net/NetworkAlertObserver.java)23
-rw-r--r--services/java/com/android/server/net/LockdownVpnTracker.java292
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java241
-rw-r--r--services/java/com/android/server/net/NetworkStatsCollection.java2
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java82
-rw-r--r--services/java/com/android/server/pm/Installer.java44
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java1878
-rw-r--r--services/java/com/android/server/pm/PackageSettingBase.java189
-rw-r--r--services/java/com/android/server/pm/PackageVerificationState.java21
-rw-r--r--services/java/com/android/server/pm/PreferredActivity.java18
-rw-r--r--services/java/com/android/server/pm/Settings.java330
-rw-r--r--services/java/com/android/server/pm/UserManager.java465
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java723
-rw-r--r--services/java/com/android/server/power/DisplayPowerController.java1160
-rw-r--r--services/java/com/android/server/power/DisplayPowerRequest.java103
-rw-r--r--services/java/com/android/server/power/DisplayPowerState.java276
-rw-r--r--services/java/com/android/server/power/ElectronBeam.java653
-rw-r--r--services/java/com/android/server/power/Notifier.java442
-rw-r--r--services/java/com/android/server/power/PhotonicModulator.java98
-rw-r--r--services/java/com/android/server/power/PowerManagerService.java2122
-rw-r--r--services/java/com/android/server/power/RampAnimator.java131
-rw-r--r--services/java/com/android/server/power/ShutdownThread.java (renamed from services/java/com/android/server/pm/ShutdownThread.java)22
-rw-r--r--services/java/com/android/server/power/SuspendBlocker.java43
-rw-r--r--services/java/com/android/server/updatable/CertPinInstallReceiver.java24
-rw-r--r--services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java265
-rw-r--r--services/java/com/android/server/usb/UsbDebuggingManager.java324
-rw-r--r--services/java/com/android/server/usb/UsbDeviceManager.java40
-rw-r--r--services/java/com/android/server/usb/UsbService.java10
-rw-r--r--services/java/com/android/server/usb/UsbSettingsManager.java7
-rw-r--r--services/java/com/android/server/wm/AppWindowAnimator.java42
-rw-r--r--services/java/com/android/server/wm/AppWindowToken.java2
-rw-r--r--services/java/com/android/server/wm/BlackFrame.java21
-rw-r--r--services/java/com/android/server/wm/DimAnimator.java34
-rw-r--r--services/java/com/android/server/wm/DimSurface.java17
-rw-r--r--services/java/com/android/server/wm/DisplayContent.java133
-rw-r--r--services/java/com/android/server/wm/DragState.java26
-rw-r--r--services/java/com/android/server/wm/FakeWindowImpl.java3
-rw-r--r--services/java/com/android/server/wm/InputMonitor.java100
-rw-r--r--services/java/com/android/server/wm/KeyguardDisableHandler.java107
-rw-r--r--services/java/com/android/server/wm/MagnificationSpec.java46
-rw-r--r--services/java/com/android/server/wm/ScreenRotationAnimation.java169
-rw-r--r--services/java/com/android/server/wm/Session.java54
-rw-r--r--services/java/com/android/server/wm/StrictModeFlash.java7
-rw-r--r--services/java/com/android/server/wm/Watermark.java11
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java740
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java3612
-rw-r--r--services/java/com/android/server/wm/WindowState.java100
-rw-r--r--services/java/com/android/server/wm/WindowStateAnimator.java371
145 files changed, 30472 insertions, 14572 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/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 32ac8e1..f960833 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -34,6 +34,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
import android.text.format.Time;
@@ -242,32 +243,39 @@ class AlarmManagerService extends IAlarmManager.Stub {
"android.permission.SET_TIME_ZONE",
"setTimeZone");
- if (TextUtils.isEmpty(tz)) return;
- TimeZone zone = TimeZone.getTimeZone(tz);
- // Prevent reentrant calls from stepping on each other when writing
- // the time zone property
- boolean timeZoneWasChanged = false;
- synchronized (this) {
- String current = SystemProperties.get(TIMEZONE_PROPERTY);
- if (current == null || !current.equals(zone.getID())) {
- if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
- timeZoneWasChanged = true;
- SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ if (TextUtils.isEmpty(tz)) return;
+ TimeZone zone = TimeZone.getTimeZone(tz);
+ // Prevent reentrant calls from stepping on each other when writing
+ // the time zone property
+ boolean timeZoneWasChanged = false;
+ synchronized (this) {
+ String current = SystemProperties.get(TIMEZONE_PROPERTY);
+ if (current == null || !current.equals(zone.getID())) {
+ if (localLOGV) {
+ Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
+ }
+ timeZoneWasChanged = true;
+ SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
+ }
+
+ // Update the kernel timezone information
+ // Kernel tracks time offsets as 'minutes west of GMT'
+ int gmtOffset = zone.getOffset(System.currentTimeMillis());
+ setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
}
-
- // Update the kernel timezone information
- // Kernel tracks time offsets as 'minutes west of GMT'
- int gmtOffset = zone.getOffset(System.currentTimeMillis());
- setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
- }
- TimeZone.setDefault(null);
-
- if (timeZoneWasChanged) {
- Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("time-zone", zone.getID());
- mContext.sendBroadcast(intent);
+ TimeZone.setDefault(null);
+
+ if (timeZoneWasChanged) {
+ Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time-zone", zone.getID());
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
}
}
@@ -303,7 +311,7 @@ class AlarmManagerService extends IAlarmManager.Stub {
}
}
}
-
+
public void removeLocked(String packageName) {
removeLocked(mRtcWakeupAlarms, packageName);
removeLocked(mRtcAlarms, packageName);
@@ -327,6 +335,29 @@ class AlarmManagerService extends IAlarmManager.Stub {
}
}
}
+
+ public void removeUserLocked(int userHandle) {
+ removeUserLocked(mRtcWakeupAlarms, userHandle);
+ removeUserLocked(mRtcAlarms, userHandle);
+ removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
+ removeUserLocked(mElapsedRealtimeAlarms, userHandle);
+ }
+
+ private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
+ if (alarmList.size() <= 0) {
+ return;
+ }
+
+ // iterator over the list removing any it where the intent match
+ Iterator<Alarm> it = alarmList.iterator();
+
+ while (it.hasNext()) {
+ Alarm alarm = it.next();
+ if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
+ it.remove();
+ }
+ }
+ }
public boolean lookForPackageLocked(String packageName) {
return lookForPackageLocked(mRtcWakeupAlarms, packageName)
@@ -637,7 +668,7 @@ class AlarmManagerService extends IAlarmManager.Stub {
Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
synchronized (mLock) {
@@ -822,6 +853,7 @@ class AlarmManagerService extends IAlarmManager.Stub {
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiver(this, sdFilter);
}
@@ -841,6 +873,11 @@ class AlarmManagerService extends IAlarmManager.Stub {
return;
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0) {
+ removeUserLocked(userHandle);
+ }
} else {
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
&& intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 38f4554..385681e 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -27,10 +27,10 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -150,13 +150,13 @@ class AppWidgetService extends IAppWidgetService.Stub
// Register for the boot completed broadcast, so we can send the
// ENABLE broacasts. If we try to send them now, they time out,
// because the system isn't ready to handle them yet.
- mContext.registerReceiver(mBroadcastReceiver,
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
// Register for configuration changes so we can update the names
// of the widgets when the locale changes.
- mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
- Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -165,75 +165,95 @@ class AppWidgetService extends IAppWidgetService.Stub
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ filter, null, null);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ sdFilter, null, null);
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
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);
+
+ IntentFilter userStopFilter = new IntentFilter();
+ userStopFilter.addAction(Intent.ACTION_USER_STOPPED);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserStopped(getSendingUserId());
+ }
+ }, UserHandle.ALL, userFilter, null, null);
}
@Override
public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
- return getImplForUser().allocateAppWidgetId(packageName, hostId);
+ return getImplForUser(UserHandle.getCallingUserId()).allocateAppWidgetId(
+ packageName, hostId);
}
@Override
public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
- getImplForUser().deleteAppWidgetId(appWidgetId);
+ getImplForUser(UserHandle.getCallingUserId()).deleteAppWidgetId(appWidgetId);
}
@Override
public void deleteHost(int hostId) throws RemoteException {
- getImplForUser().deleteHost(hostId);
+ getImplForUser(UserHandle.getCallingUserId()).deleteHost(hostId);
}
@Override
public void deleteAllHosts() throws RemoteException {
- getImplForUser().deleteAllHosts();
+ getImplForUser(UserHandle.getCallingUserId()).deleteAllHosts();
}
@Override
- public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
- getImplForUser().bindAppWidgetId(appWidgetId, provider);
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options)
+ throws RemoteException {
+ getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetId(appWidgetId, provider,
+ options);
}
@Override
public boolean bindAppWidgetIdIfAllowed(
- String packageName, int appWidgetId, ComponentName provider) throws RemoteException {
- return getImplForUser().bindAppWidgetIdIfAllowed(packageName, appWidgetId, provider);
+ String packageName, int appWidgetId, ComponentName provider, Bundle options)
+ throws RemoteException {
+ return getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetIdIfAllowed(
+ packageName, appWidgetId, provider, options);
}
@Override
public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException {
- return getImplForUser().hasBindAppWidgetPermission(packageName);
+ return getImplForUser(UserHandle.getCallingUserId()).hasBindAppWidgetPermission(
+ packageName);
}
@Override
public void setBindAppWidgetPermission(String packageName, boolean permission)
throws RemoteException {
- getImplForUser().setBindAppWidgetPermission(packageName, permission);
+ getImplForUser(UserHandle.getCallingUserId()).setBindAppWidgetPermission(
+ packageName, permission);
}
@Override
public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
throws RemoteException {
- getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
+ getImplForUser(UserHandle.getCallingUserId()).bindRemoteViewsService(
+ appWidgetId, intent, connection);
}
@Override
public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
List<RemoteViews> updatedViews) throws RemoteException {
- return getImplForUser().startListening(host, packageName, hostId, updatedViews);
+ return getImplForUser(UserHandle.getCallingUserId()).startListening(host,
+ packageName, hostId, updatedViews);
}
public void onUserRemoved(int userId) {
@@ -247,8 +267,10 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- private AppWidgetServiceImpl getImplForUser() {
- final int userId = Binder.getOrigCallingUser();
+ public void onUserStopped(int userId) {
+ }
+
+ private AppWidgetServiceImpl getImplForUser(int userId) {
AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
if (service == null) {
Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
@@ -265,27 +287,27 @@ class AppWidgetService extends IAppWidgetService.Stub
@Override
public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
- return getImplForUser().getAppWidgetIds(provider);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetIds(provider);
}
@Override
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
- return getImplForUser().getAppWidgetInfo(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetInfo(appWidgetId);
}
@Override
public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
- return getImplForUser().getAppWidgetViews(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetViews(appWidgetId);
}
@Override
public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
- getImplForUser().updateAppWidgetOptions(appWidgetId, options);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetOptions(appWidgetId, options);
}
@Override
public Bundle getAppWidgetOptions(int appWidgetId) {
- return getImplForUser().getAppWidgetOptions(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetOptions(appWidgetId);
}
static int[] getAppWidgetIds(Provider p) {
@@ -299,40 +321,43 @@ class AppWidgetService extends IAppWidgetService.Stub
@Override
public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
- return getImplForUser().getInstalledProviders();
+ return getImplForUser(UserHandle.getCallingUserId()).getInstalledProviders();
}
@Override
public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
throws RemoteException {
- getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
+ getImplForUser(UserHandle.getCallingUserId()).notifyAppWidgetViewDataChanged(
+ appWidgetIds, viewId);
}
@Override
public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
throws RemoteException {
- getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
+ getImplForUser(UserHandle.getCallingUserId()).partiallyUpdateAppWidgetIds(
+ appWidgetIds, views);
}
@Override
public void stopListening(int hostId) throws RemoteException {
- getImplForUser().stopListening(hostId);
+ getImplForUser(UserHandle.getCallingUserId()).stopListening(hostId);
}
@Override
public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
- getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
+ getImplForUser(UserHandle.getCallingUserId()).unbindRemoteViewsService(
+ appWidgetId, intent);
}
@Override
public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
- getImplForUser().updateAppWidgetIds(appWidgetIds, views);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetIds(appWidgetIds, views);
}
@Override
public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
throws RemoteException {
- getImplForUser().updateAppWidgetProvider(provider, views);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetProvider(provider, views);
}
@Override
@@ -349,16 +374,29 @@ class AppWidgetService extends IAppWidgetService.Stub
String action = intent.getAction();
// Slog.d(TAG, "received " + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- getImplForUser().sendInitialBroadcasts();
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId >= 0) {
+ getImplForUser(userId).sendInitialBroadcasts();
+ } else {
+ Slog.w(TAG, "Not user handle supplied in " + intent);
+ }
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
for (int i = 0; i < mAppWidgetServices.size(); i++) {
AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
service.onConfigurationChanged();
}
} else {
- for (int i = 0; i < mAppWidgetServices.size(); i++) {
- AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
- service.onBroadcastReceived(intent);
+ int sendingUser = getSendingUserId();
+ if (sendingUser == UserHandle.USER_ALL) {
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.onBroadcastReceived(intent);
+ }
+ } else {
+ AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
+ if (service != null) {
+ service.onBroadcastReceived(intent);
+ }
}
}
}
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index f9c432b..499c15e 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -36,24 +36,28 @@ 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.Environment;
import android.os.IBinder;
+import android.os.Process;
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 +192,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 +215,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 +238,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 +270,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 +288,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) {
@@ -295,6 +317,8 @@ class AppWidgetServiceImpl {
pw.print(info.updatePeriodMillis);
pw.print(" resizeMode=");
pw.print(info.resizeMode);
+ pw.print(info.widgetCategory);
+ pw.print(info.widgetFeatures);
pw.print(" autoAdvanceViewId=");
pw.print(info.autoAdvanceViewId);
pw.print(" initialLayout=#");
@@ -386,7 +410,7 @@ class AppWidgetServiceImpl {
}
public int allocateAppWidgetId(String packageName, int hostId) {
- int callingUid = enforceCallingUid(packageName);
+ int callingUid = enforceSystemOrCallingUid(packageName);
synchronized (mAppWidgetIds) {
ensureStateLoadedLocked();
int appWidgetId = mNextAppWidgetId++;
@@ -479,7 +503,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 +511,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));
}
}
}
@@ -506,7 +530,7 @@ class AppWidgetServiceImpl {
}
}
- private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider) {
+ private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mAppWidgetIds) {
@@ -529,6 +553,17 @@ class AppWidgetServiceImpl {
}
id.provider = p;
+ if (options == null) {
+ options = new Bundle();
+ }
+ id.options = options;
+
+ // We need to provide a default value for the widget category if it is not specified
+ if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+ }
+
p.instances.add(id);
int instancesSize = p.instances.size();
if (instancesSize == 1) {
@@ -551,14 +586,14 @@ class AppWidgetServiceImpl {
}
}
- public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
"bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
- bindAppWidgetIdImpl(appWidgetId, provider);
+ bindAppWidgetIdImpl(appWidgetId, provider, options);
}
public boolean bindAppWidgetIdIfAllowed(
- String packageName, int appWidgetId, ComponentName provider) {
+ String packageName, int appWidgetId, ComponentName provider, Bundle options) {
try {
mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
} catch (SecurityException se) {
@@ -566,14 +601,14 @@ class AppWidgetServiceImpl {
return false;
}
}
- bindAppWidgetIdImpl(appWidgetId, provider);
+ bindAppWidgetIdImpl(appWidgetId, provider, options);
return true;
}
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 +680,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 +771,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();
@@ -852,15 +887,18 @@ class AppWidgetServiceImpl {
if (id == null) {
return;
}
+
Provider p = id.provider;
- id.options = options;
+ // Merge the options
+ id.options.putAll(options);
// send the broacast saying that this appWidgetId has been deleted
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
intent.setComponent(p.info.provider);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
- mContext.sendBroadcast(intent, mUserId);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
+ saveStateLocked();
}
}
@@ -1006,7 +1044,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 +1223,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 +1231,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));
}
}
@@ -1304,6 +1342,8 @@ class AppWidgetServiceImpl {
com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
info.initialLayout = sa.getResourceId(
com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+ info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
+ AppWidgetProviderInfo_initialKeyguardLayout, 0);
String className = sa
.getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
if (className != null) {
@@ -1318,6 +1358,12 @@ class AppWidgetServiceImpl {
info.resizeMode = sa.getInt(
com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
AppWidgetProviderInfo.RESIZE_NONE);
+ info.widgetCategory = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+ info.widgetFeatures = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures,
+ AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
sa.recycle();
} catch (Exception e) {
@@ -1346,6 +1392,15 @@ class AppWidgetServiceImpl {
return pkgInfo.applicationInfo.uid;
}
+ int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
+ int callingUid = Binder.getCallingUid();
+ int uid = Process.myUid();
+ if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
+ return callingUid;
+ }
+ return enforceCallingUid(packageName);
+ }
+
int enforceCallingUid(String packageName) throws IllegalArgumentException {
int callingUid = Binder.getCallingUid();
int packageUid;
@@ -1355,7 +1410,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);
}
@@ -1455,6 +1510,18 @@ class AppWidgetServiceImpl {
if (id.provider != null) {
out.attribute(null, "p", Integer.toHexString(id.provider.tag));
}
+ if (id.options != null) {
+ out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
+ out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
+ out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
+ out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
+ out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+ }
out.endTag(null, "g");
}
@@ -1475,6 +1542,7 @@ class AppWidgetServiceImpl {
}
}
+ @SuppressWarnings("unused")
void readStateFromFileLocked(FileInputStream stream) {
boolean success = false;
try {
@@ -1494,11 +1562,12 @@ class AppWidgetServiceImpl {
String pkg = parser.getAttributeValue(null, "pkg");
String cl = parser.getAttributeValue(null, "cl");
- final PackageManager packageManager = mContext.getPackageManager();
+ final IPackageManager packageManager = AppGlobals.getPackageManager();
try {
- packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
- } catch (PackageManager.NameNotFoundException e) {
- String[] pkgs = packageManager
+ packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
+ UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ String[] pkgs = mContext.getPackageManager()
.currentToCanonicalPackageNames(new String[] { pkg });
pkg = pkgs[0];
}
@@ -1547,6 +1616,34 @@ class AppWidgetServiceImpl {
mNextAppWidgetId = id.appWidgetId + 1;
}
+ Bundle options = new Bundle();
+ String minWidthString = parser.getAttributeValue(null, "min_width");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+ Integer.parseInt(minWidthString, 16));
+ }
+ String minHeightString = parser.getAttributeValue(null, "min_height");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+ Integer.parseInt(minHeightString, 16));
+ }
+ String maxWidthString = parser.getAttributeValue(null, "max_height");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+ Integer.parseInt(maxWidthString, 16));
+ }
+ String maxHeightString = parser.getAttributeValue(null, "max_height");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+ Integer.parseInt(maxHeightString, 16));
+ }
+ String categoryString = parser.getAttributeValue(null, "host_category");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+ Integer.parseInt(categoryString, 16));
+ }
+ id.options = options;
+
String providerString = parser.getAttributeValue(null, "p");
if (providerString != null) {
// there's no provider if it hasn't been bound yet.
@@ -1613,11 +1710,11 @@ class AppWidgetServiceImpl {
}
static File getSettingsFile(int userId) {
- return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
+ return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
}
AtomicFile savedStateFile() {
- File dir = new File("/data/system/users/" + mUserId);
+ File dir = Environment.getUserSystemDirectory(mUserId);
File settingsFile = getSettingsFile(mUserId);
if (!settingsFile.exists() && mUserId == 0) {
if (!dir.exists()) {
@@ -1642,7 +1739,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 +1750,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 +1761,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 +1786,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 +1803,7 @@ class AppWidgetServiceImpl {
if (p == null) {
if (addProviderLocked(ri)) {
keep.add(ai.name);
+ providersUpdated = true;
}
} else {
Provider parsed = parseProviderInfoXml(component, ri);
@@ -1729,6 +1838,7 @@ class AppWidgetServiceImpl {
}
// Now that we've told the host, push out an update.
sendUpdateIntentLocked(p, appWidgetIds);
+ providersUpdated = true;
}
}
}
@@ -1741,17 +1851,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 +1884,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..5e2b425 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -65,7 +65,9 @@ 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.Environment.UserEnvironment;
import android.os.storage.IMountService;
import android.provider.Settings;
import android.util.EventLog;
@@ -1662,8 +1664,7 @@ class BackupManagerService extends IBackupManager.Stub {
synchronized(mClearDataLock) {
mClearingData = true;
try {
- mActivityManager.clearApplicationUserData(packageName, observer,
- Binder.getOrigCallingUser());
+ mActivityManager.clearApplicationUserData(packageName, observer, 0);
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
@@ -2720,9 +2721,13 @@ class BackupManagerService extends IBackupManager.Stub {
FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
apkDir, appSourceDir, output);
+ // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
+ // doesn't have access to external storage.
+
// Save associated .obb content if it exists and we did save the apk
// check for .obb and save those too
- final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName);
+ final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER);
+ final File obbDir = userEnv.getExternalStorageAppObbDirectory(pkg.packageName);
if (obbDir != null) {
if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
File[] obbFiles = obbDir.listFiles();
@@ -4401,6 +4406,18 @@ class BackupManagerService extends IBackupManager.Stub {
return;
}
+ if (packageInfo.applicationInfo.backupAgentName == null
+ || "".equals(packageInfo.applicationInfo.backupAgentName)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Data exists for package " + packageName
+ + " but app has no agent; skipping");
+ }
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
+ "Package has no agent");
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ return;
+ }
+
if (metaInfo.versionCode > packageInfo.versionCode) {
// Data is from a "newer" version of the app than we have currently
// installed. If the app has not declared that it is prepared to
@@ -4845,6 +4862,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 +4966,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 +5035,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..fe8529b 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -33,6 +33,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -68,7 +69,7 @@ import java.util.Arrays;
* a degree Centigrade</p>
* <p>&quot;technology&quot; - 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 +94,7 @@ class BatteryService extends Binder {
private boolean mAcOnline;
private boolean mUsbOnline;
+ private boolean mWirelessOnline;
private int mBatteryStatus;
private int mBatteryHealth;
private boolean mBatteryPresent;
@@ -114,6 +116,7 @@ class BatteryService extends Binder {
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
+ private int mShutdownBatteryTemperature;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -136,6 +139,8 @@ class BatteryService extends Binder {
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+ mShutdownBatteryTemperature = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_shutdownBatteryTemperature);
mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
@@ -148,12 +153,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 +175,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 +204,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();
@@ -217,9 +231,11 @@ class BatteryService extends Binder {
}
private final void shutdownIfOverTemp() {
- // shut down gracefully if temperature is too high (> 68.0C)
- // wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) {
+ // shut down gracefully if temperature is too high (> 68.0C by default)
+ // wait until the system has booted before attempting to display the
+ // shutdown dialog.
+ if (mBatteryTemperature > mShutdownBatteryTemperature
+ && ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -243,10 +259,12 @@ 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;
}
-
+
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
@@ -255,7 +273,7 @@ class BatteryService extends Binder {
} catch (RemoteException e) {
// Should never happen.
}
-
+
shutdownIfNoPower();
shutdownIfOverTemp();
@@ -333,21 +351,21 @@ class BatteryService extends Binder {
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (mPlugType != 0 && mLastPlugType == 0) {
statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
else if (mPlugType == 0 && mLastPlugType != 0) {
statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
if (sendBatteryLow) {
mSentLowBatteryBroadcast = true;
statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
} else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
mSentLowBatteryBroadcast = false;
statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
// Update the battery LED
@@ -398,10 +416,11 @@ class BatteryService extends Binder {
" temperature: " + mBatteryTemperature +
" technology: " + mBatteryTechnology +
" AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
+ " Wireless powered:" + mWirelessOnline +
" icon:" + icon + " invalid charger:" + mInvalidCharger);
}
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
}
private final void logBatteryStats() {
@@ -503,6 +522,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 +543,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 +625,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..e68686d
--- /dev/null
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -0,0 +1,773 @@
+/*
+ * 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.os.UserHandle;
+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
+ //Maximum msec to wait for service restart
+ private static final int SERVICE_RESTART_TIME_MS = 200;
+
+ 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_RESTART_BLUETOOTH_SERVICE = 42;
+ 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.Global.getString(resolver,
+ Settings.Global.AIRPLANE_MODE_RADIOS);
+ final String toggleableRadios = Settings.Global.getString(resolver,
+ Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+ boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true :
+ airplaneModeRadios.contains(Settings.Global.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.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.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.sendBroadcastAsUser(intent, UserHandle.ALL,
+ BLUETOOTH_PERM);
+ }
+ break;
+ }
+ case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
+ {
+ if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
+ sendBluetoothServiceDownCallback();
+
+ // Send BT state broadcast to update
+ // the BT icon correctly
+ Message stateChangeMsg = mHandler.obtainMessage(
+ MESSAGE_BLUETOOTH_STATE_CHANGE);
+ stateChangeMsg.arg1 = BluetoothAdapter.STATE_ON;
+ stateChangeMsg.arg2 =
+ BluetoothAdapter.STATE_TURNING_OFF;
+ mHandler.sendMessage(stateChangeMsg);
+ synchronized(mConnection) {
+ mBluetooth = null;
+ }
+ // Send a Bluetooth Restart message
+ Message restartMsg = mHandler.obtainMessage(
+ MESSAGE_RESTART_BLUETOOTH_SERVICE);
+ mHandler.sendMessageDelayed(restartMsg,
+ SERVICE_RESTART_TIME_MS);
+ break;
+ }
+ case MESSAGE_RESTART_BLUETOOTH_SERVICE:
+ {
+ Log.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
+ +" Restart IBluetooth service");
+ /* Enable without persisting the setting as
+ it doesnt change when IBluetooth
+ service restarts */
+ handleEnable(false, mQuietEnable);
+ 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..74ec6e2 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.ClipData;
@@ -26,6 +27,7 @@ import android.content.IOnPrimaryClipChangedListener;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -36,7 +38,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 +98,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,12 +117,11 @@ public class ClipboardService extends IClipboard.Stub {
}
private PerUserClipboard getClipboard() {
- return getClipboard(UserId.getCallingUserId());
+ return getClipboard(UserHandle.getCallingUserId());
}
private PerUserClipboard getClipboard(int userId) {
synchronized (mClipboards) {
- Slog.i(TAG, "Got clipboard for user=" + userId);
PerUserClipboard puc = mClipboards.get(userId);
if (puc == null) {
puc = new PerUserClipboard(userId);
@@ -255,15 +256,22 @@ public class ClipboardService extends IClipboard.Stub {
}
private final void addActiveOwnerLocked(int uid, String pkg) {
- PackageInfo pi;
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ final int targetUserHandle = UserHandle.getCallingUserId();
+ final long oldIdentity = Binder.clearCallingIdentity();
try {
- pi = mPm.getPackageInfo(pkg, 0);
- if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) {
+ PackageInfo pi = pm.getPackageInfo(pkg, 0, targetUserHandle);
+ if (pi == null) {
+ throw new IllegalArgumentException("Unknown package " + pkg);
+ }
+ if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {
throw new SecurityException("Calling uid " + uid
+ " does not own package " + pkg);
}
- } catch (NameNotFoundException e) {
- throw new IllegalArgumentException("Unknown package " + pkg, e);
+ } catch (RemoteException e) {
+ // Can't happen; the package manager is in the same process
+ } finally {
+ Binder.restoreCallingIdentity(oldIdentity);
}
PerUserClipboard clipboard = getClipboard();
if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(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..5c7a3ed 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,25 +16,38 @@
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;
+import android.net.CaptivePortalTracker;
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;
@@ -64,11 +77,15 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
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 +93,24 @@ 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.internal.util.IndentingPrintWriter;
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 +126,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 +148,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();
@@ -142,6 +170,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private NetworkStateTracker mNetTrackers[];
+ /* Handles captive portal check on a network */
+ private CaptivePortalTracker mCaptivePortalTracker;
+
/**
* The link properties that define the current links
*/
@@ -186,95 +217,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 Handler mHandler;
+ private static final int EVENT_VPN_STATE_CHANGED = 14;
+
+ /** 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 +347,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 +390,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 +505,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 +550,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 +611,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 +650,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 +672,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
return wimaxStateTracker;
}
+
/**
* Sets the preferred network.
* @param preference the new preference
@@ -630,7 +680,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 +800,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 +820,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 +988,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 +1090,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();
}
@@ -1291,8 +1370,10 @@ private NetworkStateTracker makeWimaxStateTracker() {
return false;
}
NetworkStateTracker tracker = mNetTrackers[networkType];
+ DetailedState netState = tracker.getNetworkInfo().getDetailedState();
- if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
+ if (tracker == null || (netState != DetailedState.CONNECTED &&
+ netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
tracker.isTeardownRequested()) {
if (VDBG) {
log("requestRouteToHostAddress on down network " +
@@ -1591,6 +1672,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 +1695,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 +1805,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 +1820,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 +1850,19 @@ 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);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+ RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Called when an attempt to fail over to another network has failed.
* @param info the {@link NetworkInfo} for the failed network
@@ -1779,6 +1883,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);
}
@@ -1829,7 +1934,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
log("sendStickyBroadcast: action=" + intent.getAction());
}
- mContext.sendStickyBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -1850,37 +1960,55 @@ private NetworkStateTracker makeWimaxStateTracker() {
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
- mContext.sendStickyBroadcast(mInitialBroadcast);
+ mContext.sendStickyBroadcastAsUser(mInitialBroadcast, UserHandle.ALL);
mInitialBroadcast = null;
}
}
// 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 boolean isNewNetTypePreferredOverCurrentNetType(int type) {
+ if ((type != mNetworkPreference &&
+ mNetConfigs[mActiveDefaultNetwork].priority >
+ mNetConfigs[type].priority) ||
+ mNetworkPreference == mActiveDefaultNetwork) return false;
+ return true;
}
private void handleConnect(NetworkInfo info) {
- final int type = info.getType();
+ final int newNetType = info.getType();
+
+ setupDataActivityTracking(newNetType);
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
- final NetworkStateTracker thisNet = mNetTrackers[type];
+ final NetworkStateTracker thisNet = mNetTrackers[newNetType];
+ final String thisIface = thisNet.getLinkProperties().getInterfaceName();
// if this is a default net and other default is running
// kill the one not preferred
- if (mNetConfigs[type].isDefault()) {
- if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
- if ((type != mNetworkPreference &&
- mNetConfigs[mActiveDefaultNetwork].priority >
- mNetConfigs[type].priority) ||
- mNetworkPreference == mActiveDefaultNetwork) {
- // don't accept this one
- if (VDBG) {
- log("Not broadcasting CONNECT_ACTION " +
- "to torn down network " + info.getTypeName());
- }
- teardown(thisNet);
- return;
- } else {
+ if (mNetConfigs[newNetType].isDefault()) {
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
+ if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
// tear down the other
NetworkStateTracker otherNet =
mNetTrackers[mActiveDefaultNetwork];
@@ -1893,6 +2021,14 @@ private NetworkStateTracker makeWimaxStateTracker() {
teardown(thisNet);
return;
}
+ } else {
+ // don't accept this one
+ if (VDBG) {
+ log("Not broadcasting CONNECT_ACTION " +
+ "to torn down network " + info.getTypeName());
+ }
+ teardown(thisNet);
+ return;
}
}
synchronized (ConnectivityService.this) {
@@ -1906,7 +2042,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
1000);
}
}
- mActiveDefaultNetwork = type;
+ mActiveDefaultNetwork = newNetType;
// this will cause us to come up initially as unconnected and switching
// to connected after our normal pause unless somebody reports us as reall
// disconnected
@@ -1918,20 +2054,99 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
thisNet.setTeardownRequested(false);
updateNetworkSettings(thisNet);
- handleConnectivityChange(type, false);
+ handleConnectivityChange(newNetType, false);
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, newNetType);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
}
}
+ private void handleCaptivePortalTrackerCheck(NetworkInfo info) {
+ if (DBG) log("Captive portal check " + info);
+ int type = info.getType();
+ final NetworkStateTracker thisNet = mNetTrackers[type];
+ if (mNetConfigs[type].isDefault()) {
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
+ if (isNewNetTypePreferredOverCurrentNetType(type)) {
+ if (DBG) log("Captive check on " + info.getTypeName());
+ mCaptivePortalTracker = CaptivePortalTracker.detect(mContext, info,
+ ConnectivityService.this);
+ return;
+ } else {
+ if (DBG) log("Tear down low priority net " + info.getTypeName());
+ teardown(thisNet);
+ return;
+ }
+ }
+ }
+
+ thisNet.captivePortalCheckComplete();
+ }
+
+ /** @hide */
+ public void captivePortalCheckComplete(NetworkInfo info) {
+ mNetTrackers[info.getType()].captivePortalCheckComplete();
+ mCaptivePortalTracker = null;
+ }
+
+ /**
+ * 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
@@ -2136,9 +2351,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
@@ -2264,7 +2479,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
* Connectivity events can happen before boot has completed ...
*/
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
// Caller must grab mDnsLock.
@@ -2374,7 +2594,8 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2383,20 +2604,28 @@ private NetworkStateTracker makeWimaxStateTracker() {
Binder.getCallingUid());
return;
}
+
+ // TODO: add locking to get atomic snapshot
pw.println();
- for (NetworkStateTracker nst : mNetTrackers) {
+ for (int i = 0; i < mNetTrackers.length; i++) {
+ final NetworkStateTracker nst = mNetTrackers[i];
if (nst != null) {
+ pw.println("NetworkStateTracker for " + getNetworkTypeName(i) + ":");
+ pw.increaseIndent();
if (nst.getNetworkInfo().isConnected()) {
pw.println("Active network: " + nst.getNetworkInfo().
getTypeName());
}
pw.println(nst.getNetworkInfo());
+ pw.println(nst.getLinkProperties());
pw.println(nst);
pw.println();
+ pw.decreaseIndent();
}
}
pw.println("Network Requester Pids:");
+ pw.increaseIndent();
for (int net : mPriorityList) {
String pidString = net + ": ";
for (Object pid : mNetRequestersPids[net]) {
@@ -2405,12 +2634,15 @@ private NetworkStateTracker makeWimaxStateTracker() {
pw.println(pidString);
}
pw.println();
+ pw.decreaseIndent();
pw.println("FeatureUsers:");
+ pw.increaseIndent();
for (Object requester : mFeatureUsers) {
pw.println(requester.toString());
}
pw.println();
+ pw.decreaseIndent();
synchronized (this) {
pw.println("NetworkTranstionWakeLock is currently " +
@@ -2424,15 +2656,17 @@ private NetworkStateTracker makeWimaxStateTracker() {
if (mInetLog != null) {
pw.println();
pw.println("Inet condition reports:");
+ pw.increaseIndent();
for(int i = 0; i < mInetLog.size(); i++) {
pw.println(mInetLog.get(i));
}
+ pw.decreaseIndent();
}
}
// 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);
}
@@ -2468,6 +2702,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
if (info.getDetailedState() ==
NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
+ } else if (info.getDetailedState() ==
+ DetailedState.CAPTIVE_PORTAL_CHECK) {
+ handleCaptivePortalTrackerCheck(info);
} else if (state == NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (state == NetworkInfo.State.SUSPENDED) {
@@ -2482,6 +2719,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 +2735,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 +2819,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;
}
}
}
@@ -2883,7 +3143,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
- mContext.sendStickyBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private static class SettingsObserver extends ContentObserver {
@@ -2907,11 +3172,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 +3229,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public boolean protectVpn(ParcelFileDescriptor socket) {
+ throwIfLockdownEnabled();
try {
int type = mActiveDefaultNetwork;
if (ConnectivityManager.isNetworkTypeValid(type)) {
@@ -2990,6 +3256,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public boolean prepareVpn(String oldPackage, String newPackage) {
+ throwIfLockdownEnabled();
return mVpn.prepare(oldPackage, newPackage);
}
@@ -3002,18 +3269,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 +3295,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
+ throwIfLockdownEnabled();
return mVpn.getLegacyVpnInfo();
}
@@ -3038,10 +3310,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 +3383,65 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
}
}
+
+ @Override
+ public boolean updateLockdownVpn() {
+ enforceSystemUid();
+
+ // 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");
+ }
+ }
+
+ private static void enforceSystemUid() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException("Only available to AID_SYSTEM");
+ }
+ }
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index ea19d6e..28a4310 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -55,6 +55,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -176,6 +177,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
static final long DEF_PASSWORD_EXPIRATION_DATE = 0;
long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
+ static final int DEF_KEYGUARD_WIDGET_DISABLED = 0; // none
+ int disableKeyguardWidgets = DEF_KEYGUARD_WIDGET_DISABLED;
+
boolean encryptionRequested = false;
boolean disableCamera = false;
@@ -285,6 +289,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, "value", Boolean.toString(disableCamera));
out.endTag(null, "disable-camera");
}
+ if (disableKeyguardWidgets != DEF_KEYGUARD_WIDGET_DISABLED) {
+ out.startTag(null, "disable-keyguard-widgets");
+ out.attribute(null, "value", Integer.toString(disableKeyguardWidgets));
+ out.endTag(null, "disable-keyguard-widgets");
+ }
}
void readFromXml(XmlPullParser parser)
@@ -570,10 +579,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
intent.putExtra("expiration", admin.passwordExpirationDate);
}
if (result != null) {
- mContext.sendOrderedBroadcast(intent, null, result, mHandler,
- Activity.RESULT_OK, null, null);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+ null, result, mHandler, Activity.RESULT_OK, null, null);
} else {
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
}
}
@@ -712,7 +721,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void sendChangedNotification() {
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(intent);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // TODO: This shouldn't be sent to all users, if DPM is per user.
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void loadSettingsLocked() {
@@ -1619,14 +1634,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else {
// Make sure KEEP_SCREEN_ON is disabled, since that
// would allow bypassing of the maximum time to lock.
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
}
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 +1682,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) {
@@ -1734,7 +1749,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
intent.setComponent(admin.info.getComponent());
- mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+ null, new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
try {
@@ -2091,6 +2107,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ /**
+ * Selectively disable keyguard widgets.
+ */
+ public void setKeyguardWidgetsDisabled(ComponentName who, int which) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_WIDGETS);
+ if ((ap.disableKeyguardWidgets & which) != which) {
+ ap.disableKeyguardWidgets |= which;
+ saveSettingsLocked();
+ }
+ syncDeviceCapabilitiesLocked();
+ }
+ }
+
+ /**
+ * Gets the disabled state for widgets in keyguard for the given admin,
+ * or the aggregate of all active admins if who is null.
+ */
+ public int getKeyguardWidgetsDisabled(ComponentName who) {
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return (admin != null) ? admin.disableKeyguardWidgets : 0;
+ }
+
+ // Determine whether or not keyguard widgets are disabled for any active admins.
+ final int N = mAdminList.size();
+ int which = 0;
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ which |= admin.disableKeyguardWidgets;
+ }
+ return which;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 0ed5189..c919595 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;
@@ -35,9 +39,12 @@ import android.os.ServiceManager;
import android.os.StatFs;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
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 +78,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 +103,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 +210,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 +236,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 +314,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 +350,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 +363,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();
@@ -375,8 +417,8 @@ public class DeviceStorageMonitorService extends Binder {
//cancel notification since memory has been freed
mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID);
- mContext.removeStickyBroadcast(mStorageLowIntent);
- mContext.sendBroadcast(mStorageOkIntent);
+ mContext.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
}
/**
@@ -392,8 +434,8 @@ public class DeviceStorageMonitorService extends Binder {
*/
private final void cancelFullNotification() {
if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
- mContext.removeStickyBroadcast(mStorageFullIntent);
- mContext.sendBroadcast(mStorageNotFullIntent);
+ mContext.removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
}
public void updateMemory() {
@@ -435,4 +477,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..65c39f2 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,16 @@ 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.os.UserHandle;
import android.provider.Settings;
-import android.server.BluetoothService;
+import android.service.dreams.IDreamManager;
import android.util.Log;
import android.util.Slog;
@@ -40,14 +46,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 +67,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 +80,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 +94,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 +107,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.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+ }
+
+ 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/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 932cba1..0b12410 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -31,6 +31,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.StatFs;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.Time;
import android.util.Slog;
@@ -157,7 +158,8 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_SEND_BROADCAST) {
- mContext.sendBroadcast((Intent)msg.obj, android.Manifest.permission.READ_LOGS);
+ mContext.sendBroadcastAsUser((Intent)msg.obj, UserHandle.OWNER,
+ android.Manifest.permission.READ_LOGS);
}
}
};
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 41f7335..840e006 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)
# ---------------------------
@@ -146,3 +148,16 @@ option java_package com.android.server
# ---------------------------
51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
+
+
+# ---------------------------
+# LockdownVpnTracker.java
+# ---------------------------
+51200 lockdown_vpn_connecting (egress_net|1)
+51201 lockdown_vpn_connected (egress_net|1)
+51202 lockdown_vpn_error (egress_net|1)
+
+# ---------------------------
+# ConfigUpdateInstallReceiver.java
+# ---------------------------
+51300 config_install_failed (dir|3)
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index fdb278d..c685473 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 {
@@ -3569,7 +3579,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
private final HashMap<String, InputMethodInfo> mMethodMap;
- private final HashMap<String, List<InputMethodSubtype>> mSubtypesMap =
+ private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
new HashMap<String, List<InputMethodSubtype>>();
public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap) {
if (methodMap == null) {
@@ -3585,18 +3595,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile);
if (!subtypeFile.exists()) {
// If "subtypes.xml" doesn't exist, create a blank file.
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- methodMap);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
} else {
- readAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile);
+ readAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
}
}
private void deleteAllInputMethodSubtypes(String imiId) {
synchronized (mMethodMap) {
- mSubtypesMap.remove(imiId);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.remove(imiId);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
@@ -3609,17 +3620,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final InputMethodSubtype subtype = additionalSubtypes[i];
if (!subtypes.contains(subtype)) {
subtypes.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
}
}
- mSubtypesMap.put(imi.getId(), subtypes);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.put(imi.getId(), subtypes);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
synchronized (mMethodMap) {
- return mSubtypesMap;
+ return mAdditionalSubtypesMap;
}
}
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..197f6ab 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,27 +16,22 @@
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 +40,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 +51,22 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
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 +75,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 +87,264 @@ 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 {
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>();
+ mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+ mBlacklist.init();
+ mLocationFudger = new LocationFudger(mContext, mLocationHandler);
+
+ synchronized (mLock) {
+ loadProvidersLocked();
+ }
+
+ mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+
+ // listen for settings changes
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateProvidersLocked();
+ }
+ }
+ });
+ mPackageMonitor.register(mContext, Looper.myLooper(), true);
+
+ updateProvidersLocked();
+ }
- private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
+ 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);
+ }
- // for prefix blacklist
- private String[] mWhitelist = new String[0];
- private String[] mBlacklist = new String[0];
+ // 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 Settings change notification
- private ContentQueryMap mSettings;
+ // bind to geocoder provider
+ mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
+ if (mGeocodeProvider == null) {
+ Slog.e(TAG, "no geocoder provider found");
+ }
+ }
/**
* 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 +356,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 +386,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 +393,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 +409,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 +428,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 +443,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 +466,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 +481,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 +495,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 +508,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 +531,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 +549,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private final class SettingsObserver implements Observer {
- 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 +574,141 @@ 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");
+ throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
+ " ACCESS_FINE_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;
+ /**
+ * Throw SecurityException if caller lacks permission to use Geofences.
+ */
+ private void checkGeofencePermission() {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
+ PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
}
-
- return true;
}
+ /**
+ * 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() {
@@ -989,7 +727,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
if (changesMade) {
- mContext.sendBroadcast(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+ UserHandle.ALL);
}
}
@@ -997,16 +736,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 +757,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 +840,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 +891,257 @@ 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;
+ checkGeofencePermission();
+ 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) {
+ checkGeofencePermission();
+ 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 +1160,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return true;
}
+ @Override
public void removeGpsStatusListener(IGpsStatusListener listener) {
synchronized (mLock) {
try {
@@ -1416,14 +1171,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 +1187,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 +1301,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 +1319,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 +1348,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 +1392,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 +1409,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 +1514,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 +1530,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 +1556,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 +1569,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 +1599,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 +1628,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderLocation(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2292,6 +1640,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void setTestProviderEnabled(String provider, boolean enabled) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2314,6 +1663,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderEnabled(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2329,6 +1679,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 +1691,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderStatus(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2351,119 +1703,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 +1718,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..32ab154 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,11 +16,7 @@
package com.android.server;
-import com.android.internal.app.IMediaContainerService;
-import com.android.internal.util.XmlUtils;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.pm.PackageManagerService;
-import com.android.server.NativeDaemonConnector.Command;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -30,6 +26,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.res.ObbInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -38,17 +35,16 @@ import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
+import android.os.Environment.UserEnvironment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcelable;
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;
@@ -61,9 +57,18 @@ import android.util.AttributeSet;
import android.util.Slog;
import android.util.Xml;
-import org.xmlpull.v1.XmlPullParser;
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.XmlUtils;
+import com.android.server.NativeDaemonConnector.Command;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.UserManagerService;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
import org.xmlpull.v1.XmlPullParserException;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -81,7 +86,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.Set;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
@@ -96,9 +100,11 @@ import javax.crypto.spec.PBEKeySpec;
class MountService extends IMountService.Stub
implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
- private static final boolean LOCAL_LOGD = false;
- private static final boolean DEBUG_UNMOUNT = false;
- private static final boolean DEBUG_EVENTS = false;
+ // TODO: listen for user creation/deletion
+
+ private static final boolean LOCAL_LOGD = true;
+ private static final boolean DEBUG_UNMOUNT = true;
+ private static final boolean DEBUG_EVENTS = true;
private static final boolean DEBUG_OBB = false;
// Disable this since it messes up long-running cryptfs operations.
@@ -166,25 +172,34 @@ class MountService extends IMountService.Stub
public static final int VolumeBadRemoval = 632;
}
- private Context mContext;
- private NativeDaemonConnector mConnector;
- private final ArrayList<StorageVolume> mVolumes = new ArrayList<StorageVolume>();
- private StorageVolume mPrimaryVolume;
- private final HashMap<String, String> mVolumeStates = new HashMap<String, String>();
- private final HashMap<String, StorageVolume> mVolumeMap = new HashMap<String, StorageVolume>();
- private String mExternalStoragePath;
+ private Context mContext;
+ private NativeDaemonConnector mConnector;
+
+ private final Object mVolumesLock = new Object();
+
+ /** When defined, base template for user-specific {@link StorageVolume}. */
+ private StorageVolume mEmulatedTemplate;
+
+ // @GuardedBy("mVolumesLock")
+ private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
+ /** Map from path to {@link StorageVolume} */
+ // @GuardedBy("mVolumesLock")
+ private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
+ /** Map from path to state */
+ // @GuardedBy("mVolumesLock")
+ private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
+
+ private volatile boolean mSystemReady = false;
+
private PackageManagerService mPms;
private boolean mUmsEnabling;
private boolean mUmsAvailable = false;
// Used as a lock for methods that register/unregister listeners.
final private ArrayList<MountServiceBinderListener> mListeners =
new ArrayList<MountServiceBinderListener>();
- private boolean mBooted = false;
private CountDownLatch mConnectedSignal = new CountDownLatch(1);
private CountDownLatch mAsecsScanned = new CountDownLatch(1);
private boolean mSendUmsConnectedOnBoot = false;
- // true if we should fake MEDIA_MOUNTED state for external storage
- private boolean mEmulateExternalStorage = false;
/**
* Private hash of currently mounted secure containers.
@@ -303,6 +318,8 @@ class MountService extends IMountService.Stub
private static final int H_UNMOUNT_PM_UPDATE = 1;
private static final int H_UNMOUNT_PM_DONE = 2;
private static final int H_UNMOUNT_MS = 3;
+ private static final int H_SYSTEM_READY = 4;
+
private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
private static final int MAX_UNMOUNT_RETRIES = 4;
@@ -437,17 +454,26 @@ class MountService extends IMountService.Stub
}
break;
}
- case H_UNMOUNT_MS : {
+ case H_UNMOUNT_MS: {
if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
}
+ case H_SYSTEM_READY: {
+ try {
+ handleSystemReady();
+ } catch (Exception ex) {
+ Slog.e(TAG, "Boot-time mount exception", ex);
+ }
+ break;
+ }
}
}
};
- final private HandlerThread mHandlerThread;
- final private Handler mHandler;
+
+ private final HandlerThread mHandlerThread;
+ private final Handler mHandler;
void waitForAsecScan() {
waitForLatch(mAsecsScanned);
@@ -476,90 +502,119 @@ class MountService extends IMountService.Stub
}
}
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ private void handleSystemReady() {
+ // Snapshot current volume states since it's not safe to call into vold
+ // while holding locks.
+ final HashMap<String, String> snapshot;
+ synchronized (mVolumesLock) {
+ snapshot = new HashMap<String, String>(mVolumeStates);
+ }
- if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
- mBooted = true;
+ for (Map.Entry<String, String> entry : snapshot.entrySet()) {
+ final String path = entry.getKey();
+ final String state = entry.getValue();
+ if (state.equals(Environment.MEDIA_UNMOUNTED)) {
+ int rc = doMountVolume(path);
+ if (rc != StorageResultCode.OperationSucceeded) {
+ Slog.e(TAG, String.format("Boot-time mount failed (%d)",
+ rc));
+ }
+ } else if (state.equals(Environment.MEDIA_SHARED)) {
/*
- * In the simulator, we need to broadcast a volume mounted event
- * to make the media scanner run.
+ * Bootstrap UMS enabled state since vold indicates
+ * the volume is shared (runtime restart while ums enabled)
*/
- if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
- notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia,
- VolumeState.Mounted);
- return;
+ notifyVolumeStateChange(null, path, VolumeState.NoMedia,
+ VolumeState.Shared);
+ }
+ }
+
+ // Push mounted state for all emulated storage
+ synchronized (mVolumesLock) {
+ for (StorageVolume volume : mVolumes) {
+ if (volume.isEmulated()) {
+ updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
}
- new Thread() {
- @Override
- public void run() {
- try {
- // it is not safe to call vold with mVolumeStates locked
- // so we make a copy of the paths and states and process them
- // outside the lock
- String[] paths;
- String[] states;
- int count;
- synchronized (mVolumeStates) {
- Set<String> keys = mVolumeStates.keySet();
- count = keys.size();
- paths = keys.toArray(new String[count]);
- states = new String[count];
- for (int i = 0; i < count; i++) {
- states[i] = mVolumeStates.get(paths[i]);
- }
- }
+ }
+ }
- for (int i = 0; i < count; i++) {
- String path = paths[i];
- String state = states[i];
-
- if (state.equals(Environment.MEDIA_UNMOUNTED)) {
- int rc = doMountVolume(path);
- if (rc != StorageResultCode.OperationSucceeded) {
- Slog.e(TAG, String.format("Boot-time mount failed (%d)",
- rc));
- }
- } else if (state.equals(Environment.MEDIA_SHARED)) {
- /*
- * Bootstrap UMS enabled state since vold indicates
- * the volume is shared (runtime restart while ums enabled)
- */
- notifyVolumeStateChange(null, path, VolumeState.NoMedia,
- VolumeState.Shared);
- }
- }
+ /*
+ * If UMS was connected on boot, send the connected event
+ * now that we're up.
+ */
+ if (mSendUmsConnectedOnBoot) {
+ sendUmsIntent(true);
+ mSendUmsConnectedOnBoot = false;
+ }
+ }
- /* notify external storage has mounted to trigger media scanner */
- if (mEmulateExternalStorage) {
- notifyVolumeStateChange(null,
- Environment.getExternalStorageDirectory().getPath(),
- VolumeState.NoMedia, VolumeState.Mounted);
- }
+ private final BroadcastReceiver mBootReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId == -1) return;
+ final UserHandle user = new UserHandle(userId);
- /*
- * If UMS was connected on boot, send the connected event
- * now that we're up.
- */
- if (mSendUmsConnectedOnBoot) {
- sendUmsIntent(true);
- mSendUmsConnectedOnBoot = false;
- }
- } catch (Exception ex) {
- Slog.e(TAG, "Boot-time mount exception", ex);
+ Slog.d(TAG, "BOOT_COMPLETED for " + user);
+
+ // Broadcast mounted volumes to newly booted user. This kicks off
+ // media scanner when a user becomes active.
+ synchronized (mVolumesLock) {
+ for (StorageVolume volume : mVolumes) {
+ final UserHandle owner = volume.getOwner();
+ final boolean ownerMatch = owner == null
+ || owner.getIdentifier() == user.getIdentifier();
+
+ final String state = mVolumeStates.get(volume.getPath());
+
+ if (ownerMatch && (Environment.MEDIA_MOUNTED.equals(state)
+ || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))) {
+ sendStorageIntent(Intent.ACTION_MEDIA_MOUNTED, volume, user);
+ }
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId == -1) return;
+ final UserHandle user = new UserHandle(userId);
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_USER_ADDED.equals(action)) {
+ synchronized (mVolumesLock) {
+ createEmulatedVolumeForUserLocked(user);
+ }
+
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ synchronized (mVolumesLock) {
+ final List<StorageVolume> toRemove = Lists.newArrayList();
+ for (StorageVolume volume : mVolumes) {
+ if (user.equals(volume.getOwner())) {
+ toRemove.add(volume);
}
}
- }.start();
- } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
- boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
- intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
- notifyShareAvailabilityChange(available);
+ for (StorageVolume volume : toRemove) {
+ removeVolumeLocked(volume);
+ }
+ }
}
}
};
+
+ private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
+ intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
+ notifyShareAvailabilityChange(available);
+ }
+ };
+
private final class MountServiceBinderListener implements IBinder.DeathRecipient {
final IMountServiceListener mListener;
@@ -590,11 +645,13 @@ class MountService extends IMountService.Stub
}
}
- private void updatePublicVolumeState(String path, String state) {
- String oldState;
- synchronized(mVolumeStates) {
+ private void updatePublicVolumeState(StorageVolume volume, String state) {
+ final String path = volume.getPath();
+ final String oldState;
+ synchronized (mVolumesLock) {
oldState = mVolumeStates.put(path, state);
}
+
if (state.equals(oldState)) {
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
state, state, path));
@@ -603,24 +660,24 @@ class MountService extends IMountService.Stub
Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
- if (path.equals(mExternalStoragePath)) {
- // Update state on PackageManager, but only of real events
- if (!mEmulateExternalStorage) {
- if (Environment.MEDIA_UNMOUNTED.equals(state)) {
- mPms.updateExternalMediaStatus(false, false);
+ // Tell PackageManager about changes to primary volume state, but only
+ // when not emulated.
+ if (volume.isPrimary() && !volume.isEmulated()) {
+ if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+ mPms.updateExternalMediaStatus(false, false);
- /*
- * Some OBBs might have been unmounted when this volume was
- * unmounted, so send a message to the handler to let it know to
- * remove those from the list of mounted OBBS.
- */
- mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
- OBB_FLUSH_MOUNT_STATE, path));
- } else if (Environment.MEDIA_MOUNTED.equals(state)) {
- mPms.updateExternalMediaStatus(true, false);
- }
+ /*
+ * Some OBBs might have been unmounted when this volume was
+ * unmounted, so send a message to the handler to let it know to
+ * remove those from the list of mounted OBBS.
+ */
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
+ OBB_FLUSH_MOUNT_STATE, path));
+ } else if (Environment.MEDIA_MOUNTED.equals(state)) {
+ mPms.updateExternalMediaStatus(true, false);
}
}
+
synchronized (mListeners) {
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
@@ -637,7 +694,6 @@ class MountService extends IMountService.Stub
}
/**
- *
* Callback from NativeDaemonConnector
*/
public void onDaemonConnected() {
@@ -661,6 +717,11 @@ class MountService extends IMountService.Stub
String path = tok[1];
String state = Environment.MEDIA_REMOVED;
+ final StorageVolume volume;
+ synchronized (mVolumesLock) {
+ volume = mVolumesByPath.get(path);
+ }
+
int st = Integer.parseInt(tok[2]);
if (st == VolumeState.NoMedia) {
state = Environment.MEDIA_REMOVED;
@@ -678,12 +739,15 @@ class MountService extends IMountService.Stub
if (state != null) {
if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
- updatePublicVolumeState(path, state);
+ updatePublicVolumeState(volume, state);
}
}
} catch (Exception e) {
Slog.e(TAG, "Error processing initial volume state", e);
- updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
+ final StorageVolume primary = getPrimaryPhysicalVolume();
+ if (primary != null) {
+ updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
+ }
}
/*
@@ -749,6 +813,13 @@ class MountService extends IMountService.Stub
Slog.e(TAG, "Failed to parse major/minor", ex);
}
+ final StorageVolume volume;
+ final String state;
+ synchronized (mVolumesLock) {
+ volume = mVolumesByPath.get(path);
+ state = mVolumeStates.get(path);
+ }
+
if (code == VoldResponseCode.VolumeDiskInserted) {
new Thread() {
@Override
@@ -772,27 +843,27 @@ class MountService extends IMountService.Stub
}
/* Send the media unmounted event first */
if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
- sendStorageIntent(Environment.MEDIA_UNMOUNTED, path);
+ updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
+ sendStorageIntent(Environment.MEDIA_UNMOUNTED, volume, UserHandle.ALL);
if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
- updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
+ updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
action = Intent.ACTION_MEDIA_REMOVED;
} else if (code == VoldResponseCode.VolumeBadRemoval) {
if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
/* Send the media unmounted event first */
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+ updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
action = Intent.ACTION_MEDIA_UNMOUNTED;
if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
- updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
+ updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
action = Intent.ACTION_MEDIA_BAD_REMOVAL;
} else {
Slog.e(TAG, String.format("Unknown code {%d}", code));
}
if (action != null) {
- sendStorageIntent(action, path);
+ sendStorageIntent(action, volume, UserHandle.ALL);
}
} else {
return false;
@@ -802,14 +873,20 @@ class MountService extends IMountService.Stub
}
private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
- String vs = getVolumeState(path);
- if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
+ final StorageVolume volume;
+ final String state;
+ synchronized (mVolumesLock) {
+ volume = mVolumesByPath.get(path);
+ state = getVolumeState(path);
+ }
+
+ if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
String action = null;
if (oldState == VolumeState.Shared && newState != oldState) {
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
- sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, path);
+ sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
}
if (newState == VolumeState.Init) {
@@ -820,22 +897,22 @@ class MountService extends IMountService.Stub
* Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
* if we're in the process of enabling UMS
*/
- if (!vs.equals(
- Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
- Environment.MEDIA_NOFS) && !vs.equals(
+ if (!state.equals(
+ Environment.MEDIA_BAD_REMOVAL) && !state.equals(
+ Environment.MEDIA_NOFS) && !state.equals(
Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+ updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
action = Intent.ACTION_MEDIA_UNMOUNTED;
}
} else if (newState == VolumeState.Pending) {
} else if (newState == VolumeState.Checking) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
- updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
+ updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
action = Intent.ACTION_MEDIA_CHECKING;
} else if (newState == VolumeState.Mounted) {
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
- updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
+ updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
action = Intent.ACTION_MEDIA_MOUNTED;
} else if (newState == VolumeState.Unmounting) {
action = Intent.ACTION_MEDIA_EJECT;
@@ -843,11 +920,11 @@ class MountService extends IMountService.Stub
} else if (newState == VolumeState.Shared) {
if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
/* Send the media unmounted event first */
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
- sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, path);
+ updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
+ sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
- updatePublicVolumeState(path, Environment.MEDIA_SHARED);
+ updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
action = Intent.ACTION_MEDIA_SHARED;
if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
} else if (newState == VolumeState.SharedMnt) {
@@ -858,13 +935,18 @@ class MountService extends IMountService.Stub
}
if (action != null) {
- sendStorageIntent(action, path);
+ sendStorageIntent(action, volume, UserHandle.ALL);
}
}
private int doMountVolume(String path) {
int rc = StorageResultCode.OperationSucceeded;
+ final StorageVolume volume;
+ synchronized (mVolumesLock) {
+ volume = mVolumesByPath.get(path);
+ }
+
if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
try {
mConnector.execute("volume", "mount", path);
@@ -884,7 +966,7 @@ class MountService extends IMountService.Stub
/*
* Media is blank or does not contain a supported filesystem
*/
- updatePublicVolumeState(path, Environment.MEDIA_NOFS);
+ updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
action = Intent.ACTION_MEDIA_NOFS;
rc = StorageResultCode.OperationFailedMediaBlank;
} else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
@@ -892,7 +974,7 @@ class MountService extends IMountService.Stub
/*
* Volume consistency check failed
*/
- updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
+ updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
action = Intent.ACTION_MEDIA_UNMOUNTABLE;
rc = StorageResultCode.OperationFailedMediaCorrupt;
} else {
@@ -903,7 +985,7 @@ class MountService extends IMountService.Stub
* Send broadcast intent (if required for the failure)
*/
if (action != null) {
- sendStorageIntent(action, path);
+ sendStorageIntent(action, volume, UserHandle.ALL);
}
}
@@ -1011,14 +1093,16 @@ class MountService extends IMountService.Stub
}
}
- if (mBooted == true) {
+ if (mSystemReady == true) {
sendUmsIntent(avail);
} else {
mSendUmsConnectedOnBoot = avail;
}
- final String path = Environment.getExternalStorageDirectory().getPath();
- if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
+ final StorageVolume primary = getPrimaryPhysicalVolume();
+ if (avail == false && primary != null
+ && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
+ final String path = primary.getPath();
/*
* USB mass storage disconnected while enabled
*/
@@ -1042,17 +1126,17 @@ class MountService extends IMountService.Stub
}
}
- private void sendStorageIntent(String action, String path) {
- Intent intent = new Intent(action, Uri.parse("file://" + path));
- // add StorageVolume extra
- intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path));
- Slog.d(TAG, "sendStorageIntent " + intent);
- mContext.sendBroadcast(intent);
+ private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
+ final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
+ intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
+ Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
+ mContext.sendBroadcastAsUser(intent, user);
}
private void sendUmsIntent(boolean c) {
- mContext.sendBroadcast(
- new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
+ mContext.sendBroadcastAsUser(
+ new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
+ UserHandle.ALL);
}
private void validatePermission(String perm) {
@@ -1065,7 +1149,10 @@ class MountService extends IMountService.Stub
private static final String TAG_STORAGE_LIST = "StorageList";
private static final String TAG_STORAGE = "storage";
- private void readStorageList() {
+ private void readStorageListLocked() {
+ mVolumes.clear();
+ mVolumeStates.clear();
+
Resources resources = mContext.getResources();
int id = com.android.internal.R.xml.storage_list;
@@ -1084,7 +1171,7 @@ class MountService extends IMountService.Stub
TypedArray a = resources.obtainAttributes(attrs,
com.android.internal.R.styleable.Storage);
- CharSequence path = a.getText(
+ String path = a.getString(
com.android.internal.R.styleable.Storage_mountPoint);
int descriptionId = a.getResourceId(
com.android.internal.R.styleable.Storage_storageDescription, -1);
@@ -1109,28 +1196,29 @@ class MountService extends IMountService.Stub
" emulated: " + emulated + " mtpReserve: " + mtpReserve +
" allowMassStorage: " + allowMassStorage +
" maxFileSize: " + maxFileSize);
- if (path == null || description == null) {
- Slog.e(TAG, "path or description is null in readStorageList");
- } else {
- String pathString = path.toString();
- StorageVolume volume = new StorageVolume(pathString,
- descriptionId, removable, emulated,
- mtpReserve, allowMassStorage, maxFileSize);
- if (primary) {
- if (mPrimaryVolume == null) {
- mPrimaryVolume = volume;
- } else {
- Slog.e(TAG, "multiple primary volumes in storage list");
- }
+
+ if (emulated) {
+ // For devices with emulated storage, we create separate
+ // volumes for each known user.
+ mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
+ true, mtpReserve, false, maxFileSize, null);
+
+ final UserManagerService userManager = UserManagerService.getInstance();
+ for (UserInfo user : userManager.getUsers()) {
+ createEmulatedVolumeForUserLocked(user.getUserHandle());
}
- if (mPrimaryVolume == volume) {
- // primay volume must be first
- mVolumes.add(0, volume);
+
+ } else {
+ if (path == null || description == null) {
+ Slog.e(TAG, "Missing storage path or description in readStorageList");
} else {
- mVolumes.add(volume);
+ final StorageVolume volume = new StorageVolume(new File(path),
+ descriptionId, primary, removable, emulated, mtpReserve,
+ allowMassStorage, maxFileSize, null);
+ addVolumeLocked(volume);
}
- mVolumeMap.put(pathString, volume);
}
+
a.recycle();
}
}
@@ -1139,48 +1227,105 @@ class MountService extends IMountService.Stub
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
- // compute storage ID for each volume
- int length = mVolumes.size();
- for (int i = 0; i < length; i++) {
- mVolumes.get(i).setStorageId(i);
+ // Compute storage ID for each physical volume; emulated storage is
+ // always 0 when defined.
+ int index = isExternalStorageEmulated() ? 1 : 0;
+ for (StorageVolume volume : mVolumes) {
+ if (!volume.isEmulated()) {
+ volume.setStorageId(index++);
+ }
}
parser.close();
}
}
/**
+ * Create and add new {@link StorageVolume} for given {@link UserHandle}
+ * using {@link #mEmulatedTemplate} as template.
+ */
+ private void createEmulatedVolumeForUserLocked(UserHandle user) {
+ if (mEmulatedTemplate == null) {
+ throw new IllegalStateException("Missing emulated volume multi-user template");
+ }
+
+ final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
+ final File path = userEnv.getExternalStorageDirectory();
+ final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
+ volume.setStorageId(0);
+ addVolumeLocked(volume);
+
+ if (mSystemReady) {
+ updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
+ } else {
+ // Place stub status for early callers to find
+ mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
+ }
+ }
+
+ private void addVolumeLocked(StorageVolume volume) {
+ Slog.d(TAG, "addVolumeLocked() " + volume);
+ mVolumes.add(volume);
+ final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
+ if (existing != null) {
+ throw new IllegalStateException(
+ "Volume at " + volume.getPath() + " already exists: " + existing);
+ }
+ }
+
+ private void removeVolumeLocked(StorageVolume volume) {
+ Slog.d(TAG, "removeVolumeLocked() " + volume);
+ mVolumes.remove(volume);
+ mVolumesByPath.remove(volume.getPath());
+ mVolumeStates.remove(volume.getPath());
+ }
+
+ private StorageVolume getPrimaryPhysicalVolume() {
+ synchronized (mVolumesLock) {
+ for (StorageVolume volume : mVolumes) {
+ if (volume.isPrimary() && !volume.isEmulated()) {
+ return volume;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* Constructs a new MountService instance
*
* @param context Binder context for this service
*/
public MountService(Context context) {
mContext = context;
- readStorageList();
- if (mPrimaryVolume != null) {
- mExternalStoragePath = mPrimaryVolume.getPath();
- mEmulateExternalStorage = mPrimaryVolume.isEmulated();
- if (mEmulateExternalStorage) {
- Slog.d(TAG, "using emulated external storage");
- mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
- }
+ synchronized (mVolumesLock) {
+ readStorageListLocked();
}
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BOOT_COMPLETED);
- // don't bother monitoring USB if mass storage is not supported on our primary volume
- if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) {
- filter.addAction(UsbManager.ACTION_USB_STATE);
- }
- mContext.registerReceiver(mBroadcastReceiver, filter, null, null);
-
mHandlerThread = new HandlerThread("MountService");
mHandlerThread.start();
mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+ // Watch for user boot completion
+ mContext.registerReceiverAsUser(mBootReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, mHandler);
+
+ // Watch for user changes
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_ADDED);
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
+
+ // Watch for USB changes on primary volume
+ final StorageVolume primary = getPrimaryPhysicalVolume();
+ if (primary != null && primary.allowMassStorage()) {
+ mContext.registerReceiver(
+ mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
+ }
+
// Add OBB Action Handler to MountService thread.
mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
@@ -1200,6 +1345,11 @@ class MountService extends IMountService.Stub
}
}
+ public void systemReady() {
+ mSystemReady = true;
+ mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
+ }
+
/**
* Exposed API calls below here
*/
@@ -1232,7 +1382,7 @@ class MountService extends IMountService.Stub
validatePermission(android.Manifest.permission.SHUTDOWN);
Slog.i(TAG, "Shutting down");
- synchronized (mVolumeStates) {
+ synchronized (mVolumesLock) {
for (String path : mVolumeStates.keySet()) {
String state = mVolumeStates.get(path);
@@ -1313,12 +1463,15 @@ class MountService extends IMountService.Stub
waitForReady();
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ final StorageVolume primary = getPrimaryPhysicalVolume();
+ if (primary == null) return;
+
// TODO: Add support for multiple share methods
/*
* If the volume is mounted and we're enabling then unmount it
*/
- String path = Environment.getExternalStorageDirectory().getPath();
+ String path = primary.getPath();
String vs = getVolumeState(path);
String method = "ums";
if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
@@ -1348,14 +1501,20 @@ class MountService extends IMountService.Stub
public boolean isUsbMassStorageEnabled() {
waitForReady();
- return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
+
+ final StorageVolume primary = getPrimaryPhysicalVolume();
+ if (primary != null) {
+ return doGetVolumeShared(primary.getPath(), "ums");
+ } else {
+ return false;
+ }
}
/**
* @return state of the volume at the specified mount point
*/
public String getVolumeState(String mountPoint) {
- synchronized (mVolumeStates) {
+ synchronized (mVolumesLock) {
String state = mVolumeStates.get(mountPoint);
if (state == null) {
Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
@@ -1370,8 +1529,9 @@ class MountService extends IMountService.Stub
}
}
+ @Override
public boolean isExternalStorageEmulated() {
- return mEmulateExternalStorage;
+ return mEmulatedTemplate != null;
}
public int mountVolume(String path) {
@@ -1437,7 +1597,9 @@ class MountService extends IMountService.Stub
}
private void warnOnNotMounted() {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ final StorageVolume primary = getPrimaryPhysicalVolume();
+ if (primary != null
+ && Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()))) {
Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
}
}
@@ -1713,7 +1875,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 = " +
@@ -1935,14 +2097,23 @@ class MountService extends IMountService.Stub
}
}
- public Parcelable[] getVolumeList() {
- synchronized(mVolumes) {
- int size = mVolumes.size();
- Parcelable[] result = new Parcelable[size];
- for (int i = 0; i < size; i++) {
- result[i] = mVolumes.get(i);
+ @Override
+ public StorageVolume[] getVolumeList() {
+ final int callingUserId = UserHandle.getCallingUserId();
+ final boolean accessAll = (mContext.checkPermission(
+ android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
+ Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
+
+ synchronized (mVolumesLock) {
+ final ArrayList<StorageVolume> filtered = Lists.newArrayList();
+ for (StorageVolume volume : mVolumes) {
+ final UserHandle owner = volume.getOwner();
+ final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
+ if (accessAll || ownerMatch) {
+ filtered.add(volume);
+ }
}
- return result;
+ return filtered.toArray(new StorageVolume[filtered.size()]);
}
}
@@ -2458,7 +2629,7 @@ class MountService extends IMountService.Stub
pw.println("");
- synchronized (mVolumes) {
+ synchronized (mVolumesLock) {
pw.println(" mVolumes:");
final int N = mVolumes.size();
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..3ddae3e 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;
@@ -44,8 +45,10 @@ import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.Binder;
import android.os.Handler;
import android.os.INetworkManagementService;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -55,7 +58,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 +96,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 +129,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 +160,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 +303,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 +371,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
}
+
+ // TODO: Push any existing firewall state
+ setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
}
//
@@ -403,6 +445,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 +835,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 +1008,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 +1051,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 +1058,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 +1073,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 +1436,79 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
- /** {@inheritDoc} */
+ @Override
+ public void setFirewallEnabled(boolean enabled) {
+ enforceSystemUid();
+ try {
+ mConnector.execute("firewall", enabled ? "enable" : "disable");
+ mFirewallEnabled = enabled;
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public boolean isFirewallEnabled() {
+ enforceSystemUid();
+ return mFirewallEnabled;
+ }
+
+ @Override
+ public void setFirewallInterfaceRule(String iface, boolean allow) {
+ enforceSystemUid();
+ 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) {
+ enforceSystemUid();
+ 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) {
+ enforceSystemUid();
+ 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) {
+ enforceSystemUid();
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_uid_rule", uid, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ private static void enforceSystemUid() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException("Only available to AID_SYSTEM");
+ }
+ }
+
+ @Override
public void monitor() {
if (mConnector != null) {
mConnector.monitor();
@@ -1338,5 +1539,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..3caba1f 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -20,7 +20,9 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
@@ -48,11 +50,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 +65,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;
@@ -314,17 +317,20 @@ public class NotificationManagerService extends INotificationManager.Stub
final int id;
final int uid;
final int initialPid;
+ final int userId;
final Notification notification;
final int score;
IBinder statusBarKey;
- NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int score, Notification notification)
+ NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
+ int userId, int score, Notification notification)
{
this.pkg = pkg;
this.tag = tag;
this.id = id;
this.uid = uid;
this.initialPid = initialPid;
+ this.userId = userId;
this.score = score;
this.notification = notification;
}
@@ -339,7 +345,7 @@ public class NotificationManagerService extends INotificationManager.Stub
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
pw.println(prefix + " contentView=" + notification.contentView);
- pw.println(prefix + " uid=" + uid);
+ pw.println(prefix + " uid=" + uid + " userId=" + userId);
pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + " sound=" + notification.sound);
@@ -426,18 +432,25 @@ public class NotificationManagerService extends INotificationManager.Stub
}
public void onClearAll() {
- cancelAll();
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
+ cancelAll(ActivityManager.getCurrentUser());
}
public void onNotificationClick(String pkg, String tag, int id) {
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
- Notification.FLAG_FOREGROUND_SERVICE, false);
+ Notification.FLAG_FOREGROUND_SERVICE, false,
+ ActivityManager.getCurrentUser());
}
public void onNotificationClear(String pkg, String tag, int id) {
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
cancelNotification(pkg, tag, id, 0,
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
- true);
+ true, ActivityManager.getCurrentUser());
}
public void onPanelRevealed() {
@@ -476,7 +489,9 @@ public class NotificationManagerService extends INotificationManager.Stub
int uid, int initialPid, String message) {
Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+ "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
- cancelNotification(pkg, tag, id, 0, 0, false);
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
+ cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
long ident = Binder.clearCallingIdentity();
try {
ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
@@ -528,7 +543,8 @@ public class NotificationManagerService extends INotificationManager.Stub
}
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
- cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
+ cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
+ UserHandle.USER_ALL);
}
}
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
@@ -541,6 +557,11 @@ public class NotificationManagerService extends INotificationManager.Stub
mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_OFFHOOK));
updateNotificationPulse();
+ } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0) {
+ cancelAllNotificationsInt(null, 0, 0, true, userHandle);
+ }
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
@@ -616,6 +637,7 @@ public class NotificationManagerService extends INotificationManager.Stub
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -844,17 +866,11 @@ public class NotificationManagerService extends INotificationManager.Stub
// Notifications
// ============================================================================
- @Deprecated
- public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
- {
- enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
- }
-
public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
- int[] idOut)
+ int[] idOut, int userId)
{
enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
- tag, id, notification, idOut);
+ tag, id, notification, idOut, userId);
}
private final static int clamp(int x, int low, int high) {
@@ -865,7 +881,7 @@ public class NotificationManagerService extends INotificationManager.Stub
// Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
// uid/pid of another application)
public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
- String tag, int id, Notification notification, int[] idOut)
+ String tag, int id, Notification notification, int[] idOut, int userId)
{
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
@@ -873,6 +889,9 @@ public class NotificationManagerService extends INotificationManager.Stub
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = ("android".equals(pkg));
+ userId = ActivityManager.handleIncomingUser(callingPid,
+ callingUid, userId, false, true, "enqueueNotification", pkg);
+
// Limit the number of notifications that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification) {
@@ -947,12 +966,12 @@ public class NotificationManagerService extends INotificationManager.Stub
synchronized (mNotificationList) {
NotificationRecord r = new NotificationRecord(pkg, tag, id,
- callingUid, callingPid,
+ callingUid, callingPid, userId,
score,
notification);
NotificationRecord old = null;
- int index = indexOfNotificationLocked(pkg, tag, id);
+ int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index < 0) {
mNotificationList.add(r);
} else {
@@ -1014,6 +1033,7 @@ public class NotificationManagerService extends INotificationManager.Stub
if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
&& (!(old != null
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
+ && (r.userId == UserHandle.USER_ALL || r.userId == userId)
&& mSystemReady) {
final AudioManager audioManager = (AudioManager) mContext
@@ -1173,12 +1193,12 @@ public class NotificationManagerService extends INotificationManager.Stub
* and none of the {@code mustNotHaveFlags}.
*/
private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
- int mustNotHaveFlags, boolean sendDelete) {
+ int mustNotHaveFlags, boolean sendDelete, int userId) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag,
mustHaveFlags, mustNotHaveFlags);
synchronized (mNotificationList) {
- int index = indexOfNotificationLocked(pkg, tag, id);
+ int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
@@ -1202,7 +1222,7 @@ public class NotificationManagerService extends INotificationManager.Stub
* {@code mustHaveFlags}.
*/
boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
- int mustNotHaveFlags, boolean doit) {
+ int mustNotHaveFlags, boolean doit, int userId) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags,
mustNotHaveFlags);
@@ -1211,6 +1231,9 @@ public class NotificationManagerService extends INotificationManager.Stub
boolean canceledSomething = false;
for (int i = N-1; i >= 0; --i) {
NotificationRecord r = mNotificationList.get(i);
+ if (userId != UserHandle.USER_ALL && r.userId != userId) {
+ continue;
+ }
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
continue;
}
@@ -1234,30 +1257,30 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
- @Deprecated
- public void cancelNotification(String pkg, int id) {
- cancelNotificationWithTag(pkg, null /* tag */, id);
- }
-
- public void cancelNotificationWithTag(String pkg, String tag, int id) {
+ public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
checkCallerIsSystemOrSameApp(pkg);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "cancelNotificationWithTag", pkg);
// Don't allow client applications to cancel foreground service notis.
cancelNotification(pkg, tag, id, 0,
Binder.getCallingUid() == Process.SYSTEM_UID
- ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
+ ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
}
- public void cancelAllNotifications(String pkg) {
+ public void cancelAllNotifications(String pkg, int userId) {
checkCallerIsSystemOrSameApp(pkg);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, true, true, "cancelAllNotifications", pkg);
+
// Calling from user space, don't allow the canceling of actively
// running foreground services.
- cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
+ cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
}
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,27 +1288,31 @@ 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)) {
+ ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+ pkg, 0, UserHandle.getCallingUserId());
+ if (!UserHandle.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
}
- } catch (PackageManager.NameNotFoundException e) {
- throw new SecurityException("Unknown package " + pkg);
+ } catch (RemoteException re) {
+ throw new SecurityException("Unknown package " + pkg + "\n" + re);
}
}
- void cancelAll() {
+ void cancelAll(int userId) {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
NotificationRecord r = mNotificationList.get(i);
+ if (r.userId != userId) {
+ continue;
+ }
+
if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
mNotificationList.remove(i);
@@ -1330,12 +1357,15 @@ public class NotificationManagerService extends INotificationManager.Stub
}
// lock on mNotificationList
- private int indexOfNotificationLocked(String pkg, String tag, int id)
+ private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
{
ArrayList<NotificationRecord> list = mNotificationList;
final int len = list.size();
for (int i=0; i<len; i++) {
NotificationRecord r = list.get(i);
+ if (r.userId != userId || r.id != id) {
+ continue;
+ }
if (tag == null) {
if (r.tag != null) {
continue;
@@ -1345,7 +1375,7 @@ public class NotificationManagerService extends INotificationManager.Stub
continue;
}
}
- if (r.id == id && r.pkg.equals(pkg)) {
+ if (r.pkg.equals(pkg)) {
return i;
}
}
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
index 87843d9..2a7a2eb 100644
--- a/services/java/com/android/server/NsdService.java
+++ b/services/java/com/android/server/NsdService.java
@@ -31,6 +31,7 @@ import android.os.HandlerThread;
import android.os.Message;
import android.os.Messenger;
import android.os.IBinder;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -448,7 +449,7 @@ public class NsdService extends INsdManager.Stub {
} else {
intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private boolean isNsdEnabled() {
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/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 78c0c12..9f53fad 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -27,7 +27,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.View;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index a7a583c..8cf273d 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -24,8 +24,10 @@ import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Slog;
@@ -45,11 +47,13 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String WALLPAPER_IMAGE_DIR = "/data/system/users/0";
+ private static final String WALLPAPER_IMAGE_DIR =
+ Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String WALLPAPER_INFO_DIR = "/data/system/users/0";
+ private static final String WALLPAPER_INFO_DIR =
+ Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
// Use old keys to keep legacy data compatibility and avoid writing two wallpapers
private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e55e7fe..90783b7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,6 +28,8 @@ import android.content.pm.IPackageManager;
import android.content.res.Configuration;
import android.media.AudioService;
import android.net.wifi.p2p.WifiP2pService;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SchedulingPolicyService;
@@ -35,9 +37,6 @@ import android.os.ServiceManager;
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;
@@ -51,11 +50,16 @@ 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.Installer;
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;
@@ -114,13 +118,16 @@ class ServerThread extends Thread {
: Integer.parseInt(factoryTestStr);
final boolean headless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
+ Installer installer = null;
AccountManagerService accountManager = null;
ContentService contentService = null;
LightsService lights = null;
PowerManagerService power = null;
+ DisplayManagerService display = null;
BatteryService battery = null;
VibratorService vibrator = null;
AlarmManagerService alarm = null;
+ MountService mountService = null;
NetworkManagementService networkManagement = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
@@ -131,11 +138,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;
@@ -143,8 +150,60 @@ class ServerThread extends Thread {
CommonTimeManagementService commonTimeMgmtService = null;
InputManagerService inputManager = null;
+ // Create a shared handler thread for UI within the system server.
+ // This thread is used by at least the following components:
+ // - WindowManagerPolicy
+ // - KeyguardViewManager
+ // - DisplayManagerService
+ HandlerThread uiHandlerThread = new HandlerThread("UI");
+ uiHandlerThread.start();
+ Handler uiHandler = new Handler(uiHandlerThread.getLooper());
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //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);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
+ }
+ }
+ });
+
+ // Create a handler thread just for the window manager to enjoy.
+ HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
+ wmHandlerThread.start();
+ Handler wmHandler = new Handler(wmHandlerThread.getLooper());
+ wmHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_DISPLAY);
+ android.os.Process.setCanSelfBackground(false);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
+ }
+ }
+ });
+
// Critical services...
+ boolean onlyCore = false;
try {
+ // Wait for installd to finished starting up so that it has a chance to
+ // create critical directories such as /data/user with the appropriate
+ // permissions. We need this to complete before we initialize other services.
+ Slog.i(TAG, "Waiting for installd to be ready.");
+ installer = new Installer();
+ installer.ping();
+
Slog.i(TAG, "Entropy Mixer");
ServiceManager.addService("entropy", new EntropyMixer());
@@ -155,6 +214,10 @@ class ServerThread extends Thread {
Slog.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
+ Slog.i(TAG, "Display Manager");
+ display = new DisplayManagerService(context, wmHandler, uiHandler);
+ ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
+
Slog.i(TAG, "Telephony Registry");
ServiceManager.addService("telephony.registry", new TelephonyRegistry(context));
@@ -164,10 +227,14 @@ class ServerThread extends Thread {
AttributeCache.init(context);
+ if (!display.waitForDefaultDisplay()) {
+ reportWtf("Timeout waiting for default display to be initialized.",
+ new Throwable());
+ }
+
Slog.i(TAG, "Package Manager");
// Only run "core" apps if we're encrypting the device.
String cryptState = SystemProperties.get("vold.decrypt");
- boolean onlyCore = false;
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
onlyCore = true;
@@ -176,7 +243,7 @@ class ServerThread extends Thread {
onlyCore = true;
}
- pm = PackageManagerService.main(context,
+ pm = PackageManagerService.main(context, installer,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
boolean firstBoot = false;
@@ -186,6 +253,11 @@ class ServerThread extends Thread {
}
ActivityManagerService.setSystemProcess();
+
+ Slog.i(TAG, "User Service");
+ ServiceManager.addService(Context.USER_SERVICE,
+ UserManagerService.getInstance());
+
mContentResolver = context.getContentResolver();
@@ -218,7 +290,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 +302,8 @@ class ServerThread extends Thread {
ActivityManagerService.self());
Slog.i(TAG, "Window Manager");
- wm = WindowManagerService.main(context, power,
+ wm = WindowManagerService.main(context, power, display,
+ uiHandler, wmHandler,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
@@ -237,6 +311,8 @@ class ServerThread extends Thread {
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
ActivityManagerService.self().setWindowManager(wm);
+ display.setWindowManager(wm);
+ display.setInputManager(inputManager);
// Skip Bluetooth if we have an emulator kernel
// TODO: Use a more reliable check to see if this product should
@@ -246,23 +322,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) {
@@ -322,7 +384,6 @@ class ServerThread extends Thread {
}
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- MountService mountService = null;
if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
try {
/*
@@ -555,7 +616,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 +648,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 +778,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,20 +802,27 @@ class ServerThread extends Thread {
w.getDefaultDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);
- power.systemReady();
+ try {
+ power.systemReady(twilight);
+ } catch (Throwable e) {
+ reportWtf("making Power Manager Service ready", e);
+ }
+
try {
pm.systemReady();
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
+
try {
- lockSettings.systemReady();
+ display.systemReady(safeMode, onlyCore);
} catch (Throwable e) {
- reportWtf("making Lock Settings Service ready", e);
+ reportWtf("making Display Manager Service ready", e);
}
// These are needed to propagate to the runnable below.
final Context contextF = context;
+ final MountService mountServiceF = mountService;
final BatteryService batteryF = battery;
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
@@ -750,6 +831,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 +845,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
@@ -776,6 +857,11 @@ class ServerThread extends Thread {
if (!headless) startSystemUi(contextF);
try {
+ if (mountServiceF != null) mountServiceF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Mount Service ready", e);
+ }
+ try {
if (batteryF != null) batteryF.systemReady();
} catch (Throwable e) {
reportWtf("making Battery Service ready", e);
@@ -811,6 +897,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 +967,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..8361477 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -25,6 +25,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
@@ -35,6 +36,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 +111,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 +244,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 +338,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 +348,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);
}
@@ -587,7 +589,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
Bundle data = new Bundle();
state.fillInNotifierBundle(data);
intent.putExtras(data);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastSignalStrengthChanged(SignalStrength signalStrength) {
@@ -605,7 +607,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
Bundle data = new Bundle();
signalStrength.fillInNotifierBundle(data);
intent.putExtras(data);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastCallStateChanged(int state, String incomingNumber) {
@@ -628,7 +630,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!TextUtils.isEmpty(incomingNumber)) {
intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
}
- mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.READ_PHONE_STATE);
}
private void broadcastDataConnectionStateChanged(int state,
@@ -661,14 +664,14 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastDataConnectionFailed(String reason, String apnType) {
Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason);
intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private boolean checkNotifyPermission(String method) {
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index f35a5af..49f39fe 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -44,6 +44,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -195,6 +196,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) {}
}
@@ -367,7 +369,7 @@ public class ThrottleService extends IThrottleManager.Stub {
}
if (mPollStickyBroadcast != null) {
- mContext.removeStickyBroadcast(mPollStickyBroadcast);
+ mContext.removeStickyBroadcastAsUser(mPollStickyBroadcast, UserHandle.ALL);
}
}
@@ -493,7 +495,7 @@ public class ThrottleService extends IThrottleManager.Stub {
onPollAlarm();
Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
- mContext.sendBroadcast(broadcast);
+ mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
}
private void onPollAlarm() {
@@ -562,7 +564,7 @@ public class ThrottleService extends IThrottleManager.Stub {
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
mPollStickyBroadcast = broadcast;
mAlarmManager.cancel(mPendingPollIntent);
@@ -620,7 +622,7 @@ public class ThrottleService extends IThrottleManager.Stub {
Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
mPolicyThrottleValue.get());
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
} // else already up!
} else {
@@ -698,7 +700,7 @@ public class ThrottleService extends IThrottleManager.Stub {
}
Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
mNotificationManager.cancel(R.drawable.stat_sys_throttled);
mWarningNotificationSent = false;
}
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..3e83baa 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,34 @@ 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.os.UserHandle;
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 +84,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;
@@ -191,7 +165,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
try {
ActivityManagerNative.getDefault().startActivityWithConfig(
null, homeIntent, null, null, null, 0, 0,
- newConfig, null);
+ newConfig, null, UserHandle.USER_CURRENT);
mHoldingConfiguration = false;
} catch (RemoteException e) {
Slog.w(TAG, e.getCause());
@@ -206,15 +180,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 +201,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 +236,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 +297,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 +345,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 {
@@ -520,7 +398,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
adjustStatusBarCarModeLocked();
if (oldAction != null) {
- mContext.sendBroadcast(new Intent(oldAction));
+ mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
}
mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
action = UiModeManager.ACTION_ENTER_CAR_MODE;
@@ -528,7 +406,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
} else if (isDeskDockState(mDockState)) {
if (!isDeskDockState(mLastBroadcastState)) {
if (oldAction != null) {
- mContext.sendBroadcast(new Intent(oldAction));
+ mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
}
mLastBroadcastState = mDockState;
action = UiModeManager.ACTION_ENTER_DESK_MODE;
@@ -554,7 +432,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
Intent intent = new Intent(action);
intent.putExtra("enableFlags", enableFlags);
intent.putExtra("disableFlags", disableFlags);
- mContext.sendOrderedBroadcast(intent, null,
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mResultReceiver, null, Activity.RESULT_OK, null, null);
// Attempting to make this transition a little more clean, we are going
// to hold off on doing a configuration change until we have finished
@@ -585,7 +463,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
if (homeIntent != null) {
try {
- mContext.startActivity(homeIntent);
+ mContext.startActivityAsUser(homeIntent, UserHandle.CURRENT);
} catch (ActivityNotFoundException e) {
}
}
@@ -651,189 +529,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 +567,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/UpdateLockService.java b/services/java/com/android/server/UpdateLockService.java
index 1ffd196..0f778cd 100644
--- a/services/java/com/android/server/UpdateLockService.java
+++ b/services/java/com/android/server/UpdateLockService.java
@@ -27,6 +27,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.TokenWatcher;
import android.os.UpdateLock;
+import android.os.UserHandle;
import android.util.Slog;
import java.io.FileDescriptor;
@@ -78,7 +79,7 @@ public class UpdateLockService extends IUpdateLock.Stub {
.putExtra(UpdateLock.NOW_IS_CONVENIENT, state)
.putExtra(UpdateLock.TIMESTAMP, System.currentTimeMillis())
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
} finally {
Binder.restoreCallingIdentity(oldIdent);
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 8a08277..b027c1f 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -16,9 +16,11 @@
package com.android.server;
-import static android.os.FileObserver.*;
import static android.os.ParcelFileDescriptor.*;
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
+import android.app.IUserSwitchObserver;
import android.app.IWallpaperManager;
import android.app.IWallpaperManagerCallback;
import android.app.PendingIntent;
@@ -31,6 +33,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -41,6 +44,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
@@ -48,7 +52,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;
@@ -77,7 +81,6 @@ import org.xmlpull.v1.XmlSerializer;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
-import com.android.server.am.ActivityManagerService;
class WallpaperManagerService extends IWallpaperManager.Stub {
static final String TAG = "WallpaperService";
@@ -90,8 +93,6 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
* restarting it vs. just reverting to the static wallpaper.
*/
static final long MIN_WALLPAPER_CRASH_TIME = 10000;
-
- static final File WALLPAPER_BASE_DIR = new File("/data/system/users");
static final String WALLPAPER = "wallpaper";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
@@ -136,7 +137,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
mWallpaper.imageWallpaperPending = false;
}
bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true,
- false, mWallpaper);
+ false, mWallpaper, null);
saveSettingsLocked(mWallpaper);
}
}
@@ -146,6 +147,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
final Context mContext;
final IWindowManager mIWindowManager;
+ final IPackageManager mIPackageManager;
final MyPackageMonitor mMonitor;
WallpaperData mLastWallpaper;
@@ -213,12 +215,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
IWallpaperService mService;
IWallpaperEngine mEngine;
WallpaperData mWallpaper;
+ IRemoteCallback mReply;
public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
mInfo = info;
mWallpaper = wallpaper;
}
-
+
+ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
if (mWallpaper.connection == this) {
@@ -234,6 +238,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ @Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
mService = null;
@@ -245,16 +250,35 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
> SystemClock.uptimeMillis()
&& mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, mWallpaper.userId);
+ clearWallpaperLocked(true, mWallpaper.userId, null);
}
}
}
}
+ @Override
public void attachEngine(IWallpaperEngine engine) {
- mEngine = engine;
+ synchronized (mLock) {
+ mEngine = engine;
+ }
}
+ @Override
+ public void engineShown(IWallpaperEngine engine) {
+ synchronized (mLock) {
+ if (mReply != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mReply.sendResult(null);
+ } catch (RemoteException e) {
+ Binder.restoreCallingIdentity(ident);
+ }
+ mReply = null;
+ }
+ }
+ }
+
+ @Override
public ParcelFileDescriptor setWallpaper(String name) {
synchronized (mLock) {
if (mWallpaper.connection == this) {
@@ -278,9 +302,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
clearWallpaperComponentLocked(wallpaper);
// Do this only for the current user's wallpaper
if (wallpaper.userId == mCurrentUserId
- && !bindWallpaperComponentLocked(comp, false, false, wallpaper)) {
+ && !bindWallpaperComponentLocked(comp, false, false,
+ wallpaper, null)) {
Slog.w(TAG, "Wallpaper no longer available; reverting to default");
- clearWallpaperLocked(false, wallpaper.userId);
+ clearWallpaperLocked(false, wallpaper.userId, null);
}
}
}
@@ -348,7 +373,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (doit) {
Slog.w(TAG, "Wallpaper uninstalled, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, wallpaper.userId);
+ clearWallpaperLocked(false, wallpaper.userId, null);
}
}
}
@@ -368,7 +393,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, wallpaper.userId);
+ clearWallpaperLocked(false, wallpaper.userId, null);
}
}
if (wallpaper.nextWallpaperComponent != null
@@ -389,14 +414,15 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
mContext = context;
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
+ mIPackageManager = AppGlobals.getPackageManager();
mMonitor = new MyPackageMonitor();
mMonitor.register(context, null, true);
- WALLPAPER_BASE_DIR.mkdirs();
- loadSettingsLocked(0);
+ getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
+ loadSettingsLocked(UserHandle.USER_OWNER);
}
private static File getWallpaperDir(int userId) {
- return new File(WALLPAPER_BASE_DIR + "/" + userId);
+ return Environment.getUserSystemDirectory(userId);
}
@Override
@@ -410,29 +436,44 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
public void systemReady() {
if (DEBUG) Slog.v(TAG, "systemReady");
- WallpaperData wallpaper = mWallpaperMap.get(0);
- switchWallpaper(wallpaper);
+ WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
+ switchWallpaper(wallpaper, null);
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
wallpaper.wallpaperObserver.startWatching();
IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(Intent.ACTION_USER_SWITCHED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
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));
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
}, userFilter);
+ try {
+ ActivityManagerNative.getDefault().registerUserSwitchObserver(
+ new IUserSwitchObserver.Stub() {
+ @Override
+ public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+ switchUser(newUserId, reply);
+ }
+
+ @Override
+ public void onUserSwitchComplete(int newUserId) throws RemoteException {
+ }
+ });
+ } catch (RemoteException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
}
String getName() {
- return mWallpaperMap.get(0).name;
+ synchronized (mLock) {
+ return mWallpaperMap.get(0).name;
+ }
}
void removeUser(int userId) {
@@ -449,7 +490,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
- void switchUser(int userId) {
+ void switchUser(int userId, IRemoteCallback reply) {
synchronized (mLock) {
mCurrentUserId = userId;
WallpaperData wallpaper = mWallpaperMap.get(userId);
@@ -460,35 +501,35 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
wallpaper.wallpaperObserver.startWatching();
}
- switchWallpaper(wallpaper);
+ switchWallpaper(wallpaper, reply);
}
}
- void switchWallpaper(WallpaperData wallpaper) {
+ void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
synchronized (mLock) {
RuntimeException e = null;
try {
ComponentName cname = wallpaper.wallpaperComponent != null ?
wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
- if (bindWallpaperComponentLocked(cname, true, false, wallpaper)) {
+ if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
return;
}
} catch (RuntimeException e1) {
e = e1;
}
Slog.w(TAG, "Failure starting previous wallpaper", e);
- clearWallpaperLocked(false, wallpaper.userId);
+ clearWallpaperLocked(false, wallpaper.userId, reply);
}
}
public void clearWallpaper() {
if (DEBUG) Slog.v(TAG, "clearWallpaper");
synchronized (mLock) {
- clearWallpaperLocked(false, UserId.getCallingUserId());
+ clearWallpaperLocked(false, UserHandle.getCallingUserId(), null);
}
}
- void clearWallpaperLocked(boolean defaultFailed, int userId) {
+ void clearWallpaperLocked(boolean defaultFailed, int userId, IRemoteCallback reply) {
WallpaperData wallpaper = mWallpaperMap.get(userId);
File f = new File(getWallpaperDir(userId), WALLPAPER);
if (f.exists()) {
@@ -501,7 +542,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (userId != mCurrentUserId) return;
if (bindWallpaperComponentLocked(defaultFailed
? wallpaper.imageWallpaperComponent
- : null, true, false, wallpaper)) {
+ : null, true, false, wallpaper, reply)) {
return;
}
} catch (IllegalArgumentException e1) {
@@ -516,21 +557,38 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
// wallpaper.
Slog.e(TAG, "Default wallpaper component not found!", e);
clearWallpaperComponentLocked(wallpaper);
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e1) {
+ }
+ }
}
- public void setDimensionHints(int width, int height) throws RemoteException {
- checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
-
- int userId = UserId.getCallingUserId();
- WallpaperData wallpaper = mWallpaperMap.get(userId);
- if (wallpaper == null) {
- throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
- }
- if (width <= 0 || height <= 0) {
- throw new IllegalArgumentException("width and height must be > 0");
+ public boolean hasNamedWallpaper(String name) {
+ synchronized (mLock) {
+ for (int i=0; i<mWallpaperMap.size(); i++) {
+ WallpaperData wd = mWallpaperMap.valueAt(i);
+ if (name.equals(wd.name)) {
+ return true;
+ }
+ }
}
+ return false;
+ }
+ public void setDimensionHints(int width, int height) throws RemoteException {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
synchronized (mLock) {
+ int userId = UserHandle.getCallingUserId();
+ WallpaperData wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+ }
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width and height must be > 0");
+ }
+
if (width != wallpaper.width || height != wallpaper.height) {
wallpaper.width = width;
wallpaper.height = height;
@@ -552,14 +610,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 +632,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 +655,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) {
@@ -608,14 +666,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
public ParcelFileDescriptor setWallpaper(String name) {
- if (DEBUG) Slog.v(TAG, "setWallpaper");
- int userId = UserId.getCallingUserId();
- WallpaperData wallpaper = mWallpaperMap.get(userId);
- if (wallpaper == null) {
- throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
- }
checkPermission(android.Manifest.permission.SET_WALLPAPER);
synchronized (mLock) {
+ if (DEBUG) Slog.v(TAG, "setWallpaper");
+ int userId = UserHandle.getCallingUserId();
+ WallpaperData wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+ }
final long ident = Binder.clearCallingIdentity();
try {
ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper);
@@ -655,18 +713,18 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
public void setWallpaperComponent(ComponentName name) {
- if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
- int userId = UserId.getCallingUserId();
- WallpaperData wallpaper = mWallpaperMap.get(userId);
- if (wallpaper == null) {
- throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
- }
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
synchronized (mLock) {
+ if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
+ int userId = UserHandle.getCallingUserId();
+ WallpaperData wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+ }
final long ident = Binder.clearCallingIdentity();
try {
wallpaper.imageWallpaperPending = false;
- bindWallpaperComponentLocked(name, false, true, wallpaper);
+ bindWallpaperComponentLocked(name, false, true, wallpaper, null);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -674,7 +732,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
- boolean fromUser, WallpaperData wallpaper) {
+ boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
// Has the component changed?
if (!force) {
@@ -710,8 +768,9 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (DEBUG) Slog.v(TAG, "Using image wallpaper");
}
}
- ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
- PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
+ int serviceUserId = wallpaper.userId;
+ ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
String msg = "Selected service does not require "
+ android.Manifest.permission.BIND_WALLPAPER
@@ -728,8 +787,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) {
// Make sure the selected service is actually a wallpaper service.
- List<ResolveInfo> ris = mContext.getPackageManager()
- .queryIntentServices(intent, PackageManager.GET_META_DATA);
+ List<ResolveInfo> ris =
+ mIPackageManager.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.GET_META_DATA, serviceUserId);
for (int i=0; i<ris.size(); i++) {
ServiceInfo rsi = ris.get(i).serviceInfo;
if (rsi.name.equals(si.name) &&
@@ -767,18 +828,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
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(
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
mContext, 0,
Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
- 0));
+ 0, null, new UserHandle(serviceUserId)));
if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
String msg = "Unable to bind service: "
+ componentName;
@@ -794,6 +850,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
wallpaper.lastDiedTime = SystemClock.uptimeMillis();
+ newConn.mReply = reply;
try {
if (wallpaper.userId == mCurrentUserId) {
if (DEBUG)
@@ -804,8 +861,8 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
} catch (RemoteException e) {
}
- } catch (PackageManager.NameNotFoundException e) {
- String msg = "Unknown component " + componentName;
+ } catch (RemoteException e) {
+ String msg = "Remote exception for " + componentName + "\n" + e;
if (fromUser) {
throw new IllegalArgumentException(msg);
}
@@ -817,6 +874,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
void detachWallpaperLocked(WallpaperData wallpaper) {
if (wallpaper.connection != null) {
+ if (wallpaper.connection.mReply != null) {
+ try {
+ wallpaper.connection.mReply.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ wallpaper.connection.mReply = null;
+ }
if (wallpaper.connection.mEngine != null) {
try {
wallpaper.connection.mEngine.destroy();
@@ -849,7 +913,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
if (!wallpaper.wallpaperUpdating) {
- bindWallpaperComponentLocked(null, false, false, wallpaper);
+ bindWallpaperComponentLocked(null, false, false, wallpaper, null);
}
}
}
@@ -867,7 +931,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
wallpaper.callbacks.finishBroadcast();
final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
}
private void checkPermission(String permission) {
@@ -878,7 +942,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
private static JournaledFile makeJournaledFile(int userId) {
- final String base = getWallpaperDir(userId) + "/" + WALLPAPER_INFO;
+ final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -1032,11 +1096,11 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (wallpaper.nextWallpaperComponent != null
&& !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
- wallpaper)) {
+ wallpaper, null)) {
// No such live wallpaper or other failure; fall back to the default
// live wallpaper (since the profile being restored indicated that the
// user had selected a live rather than static one).
- bindWallpaperComponentLocked(null, false, false, wallpaper);
+ bindWallpaperComponentLocked(null, false, false, wallpaper, null);
}
success = true;
} else {
@@ -1052,7 +1116,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success);
if (success) {
bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
- wallpaper);
+ wallpaper, null);
}
}
}
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..5c38e63 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -55,9 +56,11 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
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 +113,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;
@@ -136,8 +135,8 @@ public class WifiService extends IWifiManager.Stub {
private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
/**
- * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a
- * Settings.Secure value is not present. This timeout value is chosen as
+ * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a
+ * Settings.Global value is not present. This timeout value is chosen as
* the approximate point at which the battery drain caused by Wi-Fi
* being enabled but not active exceeds the battery drain caused by
* re-establishing a connection to the mobile data network.
@@ -161,6 +160,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;
@@ -239,7 +242,7 @@ public class WifiService extends IWifiManager.Stub {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- Slog.d(TAG, "New client listening to asynchronous messages");
+ if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
mClients.add((AsyncChannel) msg.obj);
} else {
Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
@@ -248,9 +251,9 @@ public class WifiService extends IWifiManager.Stub {
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
- Slog.d(TAG, "Send failed, client connection lost");
+ if (DBG) Slog.d(TAG, "Send failed, client connection lost");
} else {
- Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+ if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
}
mClients.remove((AsyncChannel) msg.obj);
break;
@@ -302,6 +305,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;
@@ -407,12 +414,14 @@ public class WifiService extends IWifiManager.Stub {
switch(mNetworkInfo.getDetailedState()) {
case CONNECTED:
case DISCONNECTED:
+ case CAPTIVE_PORTAL_CHECK:
evaluateTrafficStatsPolling();
resetNotification();
break;
}
} else if (intent.getAction().equals(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ noteScanEnd();
checkAndSetNotification();
}
}
@@ -424,12 +433,50 @@ public class WifiService extends IWifiManager.Stub {
mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
// Setting is in seconds
- NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+ NOTIFICATION_REPEAT_DELAY_MS = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
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
@@ -457,9 +504,9 @@ public class WifiService extends IWifiManager.Stub {
final ContentResolver cr = mContext.getContentResolver();
int wifiSavedState = 0;
try {
- wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE);
+ wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);
if(wifiSavedState == 1)
- Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0);
+ Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
} catch (Settings.SettingNotFoundException e) {
;
}
@@ -469,9 +516,9 @@ public class WifiService extends IWifiManager.Stub {
private int getPersistedWifiState() {
final ContentResolver cr = mContext.getContentResolver();
try {
- return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON);
+ return Settings.Global.getInt(cr, Settings.Global.WIFI_ON);
} catch (Settings.SettingNotFoundException e) {
- Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, WIFI_DISABLED);
+ Settings.Global.putInt(cr, Settings.Global.WIFI_ON, WIFI_DISABLED);
return WIFI_DISABLED;
}
}
@@ -519,7 +566,7 @@ public class WifiService extends IWifiManager.Stub {
private void persistWifiState(int state) {
final ContentResolver cr = mContext.getContentResolver();
mPersistWifiState.set(state);
- Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, state);
+ Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state);
}
/**
@@ -541,16 +588,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() {
@@ -570,6 +609,12 @@ public class WifiService extends IWifiManager.Stub {
"WifiService");
}
+ private void enforceConnectivityInternalPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONNECTIVITY_INTERNAL,
+ "ConnectivityService");
+ }
+
/**
* see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
* @param enable {@code true} to enable, {@code false} to disable.
@@ -595,8 +640,11 @@ public class WifiService extends IWifiManager.Stub {
*/
long ident = Binder.clearCallingIdentity();
- handleWifiToggled(enable);
- Binder.restoreCallingIdentity(ident);
+ try {
+ handleWifiToggled(enable);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
if (enable) {
if (!mIsReceiverRegistered) {
@@ -791,7 +839,18 @@ public class WifiService extends IWifiManager.Stub {
*/
public List<ScanResult> getScanResults() {
enforceAccessPermission();
- return mWifiStateMachine.syncGetScanResultsList();
+ int userId = UserHandle.getCallingUserId();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ int currentUser = ActivityManager.getCurrentUser();
+ if (userId != currentUser) {
+ return new ArrayList<ScanResult>();
+ } else {
+ return mWifiStateMachine.syncGetScanResultsList();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -874,7 +933,7 @@ public class WifiService extends IWifiManager.Stub {
*
*/
public void startWifi() {
- enforceChangePermission();
+ enforceConnectivityInternalPermission();
/* TODO: may be add permissions for access only to connectivity service
* TODO: if a start issued, keep wifi alive until a stop issued irrespective
* of WifiLock & device idle status unless wifi enabled status is toggled
@@ -884,20 +943,24 @@ public class WifiService extends IWifiManager.Stub {
mWifiStateMachine.reconnectCommand();
}
+ public void captivePortalCheckComplete() {
+ enforceConnectivityInternalPermission();
+ mWifiStateMachine.captivePortalCheckComplete();
+ }
+
/**
* see {@link android.net.wifi.WifiManager#stopWifi}
*
*/
public void stopWifi() {
- enforceChangePermission();
- /* TODO: may be add permissions for access only to connectivity service
+ enforceConnectivityInternalPermission();
+ /*
* TODO: if a stop is issued, wifi is brought up only by startWifi
* unless wifi enabled status is toggled
*/
mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
}
-
/**
* see {@link android.net.wifi.WifiManager#addToBlacklist}
*
@@ -923,10 +986,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);
@@ -953,11 +1012,11 @@ public class WifiService extends IWifiManager.Stub {
String action = intent.getAction();
long idleMillis =
- Settings.Secure.getLong(mContext.getContentResolver(),
- Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
+ Settings.Global.getLong(mContext.getContentResolver(),
+ Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
int stayAwakeConditions =
- Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
if (action.equals(Intent.ACTION_SCREEN_ON)) {
if (DBG) {
Slog.d(TAG, "ACTION_SCREEN_ON");
@@ -1011,12 +1070,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,
@@ -1142,17 +1195,17 @@ public class WifiService extends IWifiManager.Stub {
}
private boolean isAirplaneSensitive() {
- String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_RADIOS);
+ String airplaneModeRadios = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_RADIOS);
return airplaneModeRadios == null
- || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
+ || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI);
}
private boolean isAirplaneToggleable() {
- String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+ String toggleableRadios = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
return toggleableRadios != null
- && toggleableRadios.contains(Settings.System.RADIO_WIFI);
+ && toggleableRadios.contains(Settings.Global.RADIO_WIFI);
}
/**
@@ -1161,8 +1214,8 @@ public class WifiService extends IWifiManager.Stub {
* @return {@code true} if airplane mode is on.
*/
private boolean isAirplaneModeOn() {
- return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) == 1;
+ return isAirplaneSensitive() && Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
}
@Override
@@ -1176,8 +1229,8 @@ public class WifiService extends IWifiManager.Stub {
}
pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
pw.println("Stay-awake conditions: " +
- Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
pw.println();
pw.println("Internal state:");
@@ -1207,13 +1260,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 +1379,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 +1389,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;
}
}
@@ -1753,8 +1795,8 @@ public class WifiService extends IWifiManager.Stub {
public void register() {
ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
mNotificationEnabled = getValue();
}
@@ -1767,8 +1809,8 @@ public class WifiService extends IWifiManager.Stub {
}
private boolean getValue() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
}
}
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..f1a03de 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -16,68 +16,59 @@
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;
-/**
- * Input filter for accessibility.
- *
- * Currently just a stub but will eventually implement touch exploration, etc.
- */
-public class AccessibilityInputFilter extends InputFilter {
- private static final String TAG = "AccessibilityInputFilter";
+class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
+
+ private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
+
private static final boolean DEBUG = false;
+ private static final int UNDEFINED_DEVICE_ID = -1;
+
+ /**
+ * Flag for enabling the screen magnification feature.
+ *
+ * @see #setEnabledFeatures(int)
+ */
+ static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
+
+ /**
+ * Flag for enabling the touch exploration feature.
+ *
+ * @see #setEnabledFeatures(int)
+ */
+ static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002;
+
private final Context mContext;
private final PowerManager mPm;
private final AccessibilityManagerService mAms;
- /**
- * This is an interface for explorers that take a {@link MotionEvent}
- * stream and perform touch exploration of the screen content.
- */
- public interface Explorer {
- /**
- * Handles a {@link MotionEvent}.
- *
- * @param event The event to handle.
- * @param policyFlags The policy flags associated with the event.
- */
- public void onMotionEvent(MotionEvent event, int policyFlags);
-
- /**
- * Requests that the explorer clears its internal state.
- *
- * @param event The last received event.
- * @param policyFlags The policy flags associated with the event.
- */
- public void clear(MotionEvent event, int policyFlags);
-
- /**
- * Requests that the explorer clears its internal state.
- */
- public void clear();
- }
+ private int mCurrentDeviceId;
- private TouchExplorer mTouchExplorer;
+ private boolean mInstalled;
- private int mTouchscreenSourceDeviceId;
+ private int mEnabledFeatures;
+
+ private TouchExplorer mTouchExplorer;
+ private ScreenMagnifier mScreenMagnifier;
+ private EventStreamTransformation mEventHandler;
- public AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
+ AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
super(context.getMainLooper());
mContext = context;
mAms = service;
- mPm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
}
@Override
@@ -85,7 +76,9 @@ public class AccessibilityInputFilter extends InputFilter {
if (DEBUG) {
Slog.d(TAG, "Accessibility input filter installed.");
}
- mTouchExplorer = new TouchExplorer(this, mContext, mAms);
+ mInstalled = true;
+ disableFeatures();
+ enableFeatures();
super.onInstalled();
}
@@ -94,7 +87,8 @@ public class AccessibilityInputFilter extends InputFilter {
if (DEBUG) {
Slog.d(TAG, "Accessibility input filter uninstalled.");
}
- mTouchExplorer.clear();
+ mInstalled = false;
+ disableFeatures();
super.onUninstalled();
}
@@ -104,27 +98,104 @@ public class AccessibilityInputFilter extends InputFilter {
Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
- if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
- MotionEvent motionEvent = (MotionEvent) event;
- int deviceId = event.getDeviceId();
- if (mTouchscreenSourceDeviceId != deviceId) {
- mTouchscreenSourceDeviceId = deviceId;
- mTouchExplorer.clear(motionEvent, policyFlags);
+ if (mEventHandler == null) {
+ super.onInputEvent(event, policyFlags);
+ return;
+ }
+ if (event.getSource() != InputDevice.SOURCE_TOUCHSCREEN) {
+ super.onInputEvent(event, policyFlags);
+ return;
+ }
+ if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
+ mEventHandler.clear();
+ super.onInputEvent(event, policyFlags);
+ return;
+ }
+ final int deviceId = event.getDeviceId();
+ if (mCurrentDeviceId != deviceId) {
+ if (mCurrentDeviceId != UNDEFINED_DEVICE_ID) {
+ mEventHandler.clear();
}
- if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) != 0) {
- mPm.userActivity(event.getEventTime(), false);
- mTouchExplorer.onMotionEvent(motionEvent, policyFlags);
+ mCurrentDeviceId = deviceId;
+ }
+ mPm.userActivity(event.getEventTime(), false);
+ MotionEvent motionEvent = (MotionEvent) event;
+ mEventHandler.onMotionEvent(motionEvent, policyFlags);
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ sendInputEvent(event, policyFlags);
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ // TODO Implement this to inject the accessibility event
+ // into the accessibility manager service similarly
+ // to how this is done for input events.
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation sink) {
+ /* do nothing */
+ }
+
+ @Override
+ public void clear() {
+ /* do nothing */
+ }
+
+ void setEnabledFeatures(int enabledFeatures) {
+ if (mEnabledFeatures == enabledFeatures) {
+ return;
+ }
+ if (mInstalled) {
+ disableFeatures();
+ }
+ mEnabledFeatures = enabledFeatures;
+ if (mInstalled) {
+ enableFeatures();
+ }
+ }
+
+ void notifyAccessibilityEvent(AccessibilityEvent event) {
+ if (mEventHandler != null) {
+ mEventHandler.onAccessibilityEvent(event);
+ }
+ }
+
+ private void enableFeatures() {
+ if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
+ mEventHandler = mScreenMagnifier = new ScreenMagnifier(mContext);
+ mEventHandler.setNext(this);
+ }
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ mTouchExplorer = new TouchExplorer(mContext, mAms);
+ mTouchExplorer.setNext(this);
+ if (mEventHandler != null) {
+ mEventHandler.setNext(mTouchExplorer);
} else {
- mTouchExplorer.clear(motionEvent, policyFlags);
+ mEventHandler = mTouchExplorer;
}
- } else {
- super.onInputEvent(event, policyFlags);
}
}
- public void onAccessibilityEvent(AccessibilityEvent event) {
+ private void disableFeatures() {
if (mTouchExplorer != null) {
- mTouchExplorer.onAccessibilityEvent(event);
+ mTouchExplorer.clear();
+ mTouchExplorer.onDestroy();
+ mTouchExplorer = null;
+ }
+ if (mScreenMagnifier != null) {
+ mScreenMagnifier.clear();
+ mScreenMagnifier.onDestroy();
+ mScreenMagnifier = null;
}
+ mEventHandler = null;
+ }
+
+ @Override
+ public void onDestroy() {
+ /* ignore */
}
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index e42ec84..e7f3599 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -48,6 +48,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -58,9 +59,11 @@ 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;
+import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
@@ -74,7 +77,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;
@@ -115,6 +117,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private static final int MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER = 3;
+ private static final int MSG_SEND_UPDATE_INPUT_FILTER = 4;
+
private static int sIdCounter = 0;
private static int sNextWindowId;
@@ -157,20 +161,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private boolean mIsTouchExplorationEnabled;
- private final WindowManagerService mWindowManagerService;
+ private boolean mIsScreenMagnificationEnabled;
+
+ private final IWindowManager mWindowManager;
private final SecurityPolicy mSecurityPolicy;
- private final MainHanler mMainHandler;
+ private final MainHandler mMainHandler;
private Service mUiAutomationService;
private Service mQueryBridge;
- private boolean mTouchExplorationGestureEnded;
-
- private boolean mTouchExplorationGestureStarted;
-
private AlertDialog mEnableTouchExplorationDialog;
/**
@@ -181,10 +183,9 @@ 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();
+ mMainHandler = new MainHandler(mContext.getMainLooper());
registerPackageChangeAndBootCompletedBroadcastReceiver();
registerSettingsContentObservers();
}
@@ -202,7 +203,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
synchronized (mLock) {
// We will update when the automation service dies.
if (mUiAutomationService == null) {
- populateAccessibilityServiceListLocked();
+ populateInstalledAccessibilityServiceLocked();
manageServicesLocked();
}
}
@@ -263,18 +264,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
synchronized (mLock) {
// We will update when the automation service dies.
if (mUiAutomationService == null) {
- populateAccessibilityServiceListLocked();
- populateEnabledAccessibilityServicesLocked();
- populateTouchExplorationGrantedAccessibilityServicesLocked();
- handleAccessibilityEnabledSettingChangedLocked();
- handleTouchExplorationEnabledSettingChangedLocked();
- updateInputFilterLocked();
- sendStateToClientsLocked();
+ updateInternalStateLocked();
}
}
return;
}
-
super.onReceive(context, intent);
}
};
@@ -330,6 +324,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
});
+ Uri accessibilityScreenMagnificationEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ contentResolver.registerContentObserver(accessibilityScreenMagnificationEnabledUri, false,
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ synchronized (mLock) {
+ // We will update when the automation service dies.
+ if (mUiAutomationService == null) {
+ handleScreenMagnificationEnabledSettingChangedLocked();
+ updateInputFilterLocked();
+ sendStateToClientsLocked();
+ }
+ }
+ }
+ });
+
Uri accessibilityServicesUri =
Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
contentResolver.registerContentObserver(accessibilityServicesUri, false,
@@ -358,8 +370,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// We will update when the automation service dies.
if (mUiAutomationService == null) {
populateTouchExplorationGrantedAccessibilityServicesLocked();
- unbindAllServicesLocked();
- manageServicesLocked();
+ handleTouchExplorationGrantedAccessibilityServicesChangedLocked();
}
}
}
@@ -385,18 +396,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
public boolean sendAccessibilityEvent(AccessibilityEvent event) {
- final int eventType = event.getEventType();
-
- // The event for gesture start should be strictly before the
- // first hover enter event for the gesture.
- if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
- && mTouchExplorationGestureStarted) {
- mTouchExplorationGestureStarted = false;
- AccessibilityEvent gestureStartEvent = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
- sendAccessibilityEvent(gestureStartEvent);
- }
-
synchronized (mLock) {
if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
mSecurityPolicy.updateActiveWindowAndEventSourceLocked(event);
@@ -406,22 +405,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (mHasInputFilter && mInputFilter != null) {
mMainHandler.obtainMessage(MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER,
AccessibilityEvent.obtain(event)).sendToTarget();
-
}
event.recycle();
mHandledFeedbackTypes = 0;
}
-
- // The event for gesture end should be strictly after the
- // last hover exit event for the gesture.
- if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
- && mTouchExplorationGestureEnded) {
- mTouchExplorationGestureEnded = false;
- AccessibilityEvent gestureEndEvent = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
- sendAccessibilityEvent(gestureEndEvent);
- }
-
return (OWN_PROCESS_ID != Binder.getCallingPid());
}
@@ -584,15 +571,24 @@ 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 {
+ WindowInfo info = mWindowManager.getWindowInfo(token);
+ if (info != null) {
+ outBounds.set(info.frame);
+ return true;
+ }
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ return false;
}
}
- int getActiveWindowId() {
+ public int getActiveWindowId() {
return mSecurityPolicy.mActiveWindowId;
}
@@ -604,14 +600,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return mQueryBridge;
}
- public void touchExplorationGestureEnded() {
- mTouchExplorationGestureEnded = true;
- }
-
- public void touchExplorationGestureStarted() {
- mTouchExplorationGestureStarted = true;
- }
-
private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
// TODO: Now we are giving the gestures to the last enabled
// service that can handle them which is the last one
@@ -624,7 +612,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;
}
@@ -648,7 +636,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
/**
* Populates the cached list of installed {@link AccessibilityService}s.
*/
- private void populateAccessibilityServiceListLocked() {
+ private void populateInstalledAccessibilityServiceLocked() {
mInstalledServices.clear();
List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
@@ -658,11 +646,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
for (int i = 0, count = installedServices.size(); i < count; i++) {
ResolveInfo resolveInfo = installedServices.get(i);
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- // For now we are enforcing this if the target version is JellyBean or
- // higher and in a later release we will enforce this for everyone.
- if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN
- && !android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
- serviceInfo.permission)) {
+ if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(serviceInfo.permission)) {
Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+ ": it does not require the permission "
@@ -958,23 +942,26 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
/**
- * Updates the touch exploration state.
+ * Updates the state of the input filter.
*/
private void updateInputFilterLocked() {
- if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
- if (!mHasInputFilter) {
- mHasInputFilter = true;
- if (mInputFilter == null) {
- mInputFilter = new AccessibilityInputFilter(mContext, this);
- }
- mWindowManagerService.setInputFilter(mInputFilter);
- }
- return;
- }
- if (mHasInputFilter) {
- mHasInputFilter = false;
- mWindowManagerService.setInputFilter(null);
- }
+ mMainHandler.obtainMessage(MSG_SEND_UPDATE_INPUT_FILTER).sendToTarget();
+ }
+
+ /**
+ * Updated the internal state of this service to match the current settings.
+ */
+ private void updateInternalStateLocked() {
+ populateInstalledAccessibilityServiceLocked();
+ populateEnabledAccessibilityServicesLocked();
+ populateTouchExplorationGrantedAccessibilityServicesLocked();
+
+ handleTouchExplorationEnabledSettingChangedLocked();
+ handleScreenMagnificationEnabledSettingChangedLocked();
+ handleAccessibilityEnabledSettingChangedLocked();
+
+ updateInputFilterLocked();
+ sendStateToClientsLocked();
}
/**
@@ -1000,6 +987,31 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
}
+ /**
+ * Updates the state based on the screen magnification enabled setting.
+ */
+ private void handleScreenMagnificationEnabledSettingChangedLocked() {
+ mIsScreenMagnificationEnabled = Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_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(
@@ -1055,7 +1067,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
- private class MainHanler extends Handler {
+ private class MainHandler extends Handler {
+
+ public MainHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
final int type = msg.what;
@@ -1112,10 +1129,50 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
case MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER: {
AccessibilityEvent event = (AccessibilityEvent) msg.obj;
if (mHasInputFilter && mInputFilter != null) {
- mInputFilter.onAccessibilityEvent(event);
+ mInputFilter.notifyAccessibilityEvent(event);
}
event.recycle();
} break;
+ case MSG_SEND_UPDATE_INPUT_FILTER: {
+ boolean setInputFilter = false;
+ AccessibilityInputFilter inputFilter = null;
+ synchronized (mLock) {
+ if ((mIsAccessibilityEnabled && mIsTouchExplorationEnabled)
+ || mIsScreenMagnificationEnabled) {
+ if (!mHasInputFilter) {
+ mHasInputFilter = true;
+ if (mInputFilter == null) {
+ mInputFilter = new AccessibilityInputFilter(mContext,
+ AccessibilityManagerService.this);
+ }
+ inputFilter = mInputFilter;
+ setInputFilter = true;
+ }
+ int flags = 0;
+ if (mIsScreenMagnificationEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
+ }
+ if (mIsTouchExplorationEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
+ }
+ mInputFilter.setEnabledFeatures(flags);
+ } else {
+ if (mHasInputFilter) {
+ mHasInputFilter = false;
+ mInputFilter.setEnabledFeatures(0);
+ inputFilter = null;
+ setInputFilter = true;
+ }
+ }
+ }
+ if (setInputFilter) {
+ try {
+ mWindowManager.setInputFilter(inputFilter);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } break;
}
}
}
@@ -1163,8 +1220,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean mCanRetrieveScreenContent;
- boolean mReqeustTouchExplorationMode;
-
boolean mIsAutomation;
final Rect mTempBounds = new Rect();
@@ -1204,7 +1259,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 +1389,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 +1401,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 +1408,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 +1417,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1377,8 +1426,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 +1439,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 +1456,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1421,8 +1465,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 +1478,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 +1485,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 +1494,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1465,8 +1503,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 +1516,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 +1532,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1508,8 +1541,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 +1554,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 +1570,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1630,18 +1658,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// the state based on values in the settings database.
if (mIsAutomation) {
mUiAutomationService = null;
-
- populateEnabledAccessibilityServicesLocked();
- populateTouchExplorationGrantedAccessibilityServicesLocked();
-
- handleAccessibilityEnabledSettingChangedLocked();
- sendStateToClientsLocked();
-
- handleTouchExplorationEnabledSettingChangedLocked();
- updateInputFilterLocked();
-
- populateAccessibilityServiceListLocked();
- manageServicesLocked();
+ updateInternalStateLocked();
}
}
}
@@ -1820,7 +1837,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 +1961,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/EventStreamTransformation.java b/services/java/com/android/server/accessibility/EventStreamTransformation.java
new file mode 100644
index 0000000..b715570
--- /dev/null
+++ b/services/java/com/android/server/accessibility/EventStreamTransformation.java
@@ -0,0 +1,90 @@
+/*
+ ** Copyright 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.accessibility;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Interface for classes that can handle and potentially transform a stream of
+ * motion and accessibility events. Instances implementing this interface are
+ * ordered in a sequence to implement a transformation chain. An instance may
+ * consume, modify, and generate events. It is responsible to deliver the
+ * output events to the next transformation in the sequence set via
+ * {@link #setNext(EventStreamTransformation)}.
+ *
+ * Note that since instances implementing this interface are transformations
+ * of the event stream, an instance should work against the event stream
+ * potentially modified by previous ones. Hence, the order of transformations
+ * is important.
+ *
+ * It is a responsibility of each handler that decides to react to an event
+ * sequence and prevent any subsequent ones from performing an action to send
+ * the appropriate cancel event given it has delegated a part of the events
+ * that belong to the current gesture. This will ensure that subsequent
+ * transformations will not be left in an inconsistent state and the applications
+ * see a consistent event stream.
+ *
+ * For example, to cancel a {@link KeyEvent} the handler has to emit an event
+ * with action {@link KeyEvent#ACTION_UP} with the additional flag
+ * {@link KeyEvent#FLAG_CANCELED}. To cancel a {@link MotionEvent} the handler
+ * has to send an event with action {@link MotionEvent#ACTION_CANCEL}.
+ *
+ * It is a responsibility of each handler that received a cancel event to clear its
+ * internal state and to propagate the event to the next one to enable subsequent
+ * transformations to clear their internal state.
+ *
+ * It is a responsibility for each transformation to start handling events only
+ * after an event that designates the start of a well-formed event sequence.
+ * For example, if it received a down motion event followed by a cancel motion
+ * event, it should not handle subsequent move and up events until it gets a down.
+ */
+interface EventStreamTransformation {
+
+ /**
+ * Receives a motion event.
+ *
+ * @param event The motion event.
+ * @param policyFlags Policy flags for the event.
+ */
+ public void onMotionEvent(MotionEvent event, int policyFlags);
+
+ /**
+ * Receives an accessibility event.
+ *
+ * @param event The accessibility event.
+ */
+ public void onAccessibilityEvent(AccessibilityEvent event);
+
+ /**
+ * Sets the next transformation.
+ *
+ * @param next The next transformation.
+ */
+ public void setNext(EventStreamTransformation next);
+
+ /**
+ * Clears the internal state of this transformation.
+ */
+ public void clear();
+
+ /**
+ * Destroys this transformation.
+ */
+ public void onDestroy();
+}
diff --git a/services/java/com/android/server/accessibility/GestureUtils.java b/services/java/com/android/server/accessibility/GestureUtils.java
new file mode 100644
index 0000000..b68b09f
--- /dev/null
+++ b/services/java/com/android/server/accessibility/GestureUtils.java
@@ -0,0 +1,102 @@
+package com.android.server.accessibility;
+
+import android.util.MathUtils;
+import android.view.MotionEvent;
+
+/**
+ * Some helper functions for gesture detection.
+ */
+final class GestureUtils {
+
+ private GestureUtils() {
+ /* cannot be instantiated */
+ }
+
+ public static boolean isTap(MotionEvent down, MotionEvent up, int tapTimeSlop,
+ int tapDistanceSlop, int actionIndex) {
+ return eventsWithinTimeAndDistanceSlop(down, up, tapTimeSlop, tapDistanceSlop, actionIndex);
+ }
+
+ public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp,
+ int multiTapTimeSlop, int multiTapDistanceSlop, int actionIndex) {
+ return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop,
+ multiTapDistanceSlop, actionIndex);
+ }
+
+ private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second,
+ int timeout, int distance, int actionIndex) {
+ if (isTimedOut(first, second, timeout)) {
+ return false;
+ }
+ final double deltaMove = computeDistance(first, second, actionIndex);
+ if (deltaMove >= distance) {
+ return false;
+ }
+ return true;
+ }
+
+ public static double computeDistance(MotionEvent first, MotionEvent second, int pointerIndex) {
+ return MathUtils.dist(first.getX(pointerIndex), first.getY(pointerIndex),
+ second.getX(pointerIndex), second.getY(pointerIndex));
+ }
+
+ public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
+ final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
+ return (deltaTime >= timeout);
+ }
+
+ public static boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
+ return (first.getPointerIdBits() == second.getPointerIdBits()
+ && first.getPointerId(first.getActionIndex())
+ == second.getPointerId(second.getActionIndex()));
+ }
+
+ /**
+ * Determines whether a two pointer gesture is a dragging one.
+ *
+ * @param event The event with the pointer data.
+ * @return True if the gesture is a dragging one.
+ */
+ public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY,
+ float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY,
+ float secondPtrX, float secondPtrY, float maxDraggingAngleCos) {
+
+ // Check if the pointers are moving in the same direction.
+ final float firstDeltaX = firstPtrX - firstPtrDownX;
+ final float firstDeltaY = firstPtrY - firstPtrDownY;
+
+ if (firstDeltaX == 0 && firstDeltaY == 0) {
+ return true;
+ }
+
+ final float firstMagnitude =
+ (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY);
+ final float firstXNormalized =
+ (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
+ final float firstYNormalized =
+ (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
+
+ final float secondDeltaX = secondPtrX - secondPtrDownX;
+ final float secondDeltaY = secondPtrY - secondPtrDownY;
+
+ if (secondDeltaX == 0 && secondDeltaY == 0) {
+ return true;
+ }
+
+ final float secondMagnitude =
+ (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY);
+ final float secondXNormalized =
+ (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
+ final float secondYNormalized =
+ (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
+
+ final float angleCos =
+ firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
+
+ if (angleCos < maxDraggingAngleCos) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java
new file mode 100644
index 0000000..48781ac
--- /dev/null
+++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -0,0 +1,1798 @@
+/*
+ * 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.accessibility;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Property;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.Gravity;
+import android.view.IDisplayContentChangeListener;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.ScaleGestureDetector;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.WindowInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * This class handles the screen magnification when accessibility is enabled.
+ * The behavior is as follows:
+ *
+ * 1. Triple tap toggles permanent screen magnification which is magnifying
+ * the area around the location of the triple tap. One can think of the
+ * location of the triple tap as the center of the magnified viewport.
+ * For example, a triple tap when not magnified would magnify the screen
+ * and leave it in a magnified state. A triple tapping when magnified would
+ * clear magnification and leave the screen in a not magnified state.
+ *
+ * 2. Triple tap and hold would magnify the screen if not magnified and enable
+ * viewport dragging mode until the finger goes up. One can think of this
+ * mode as a way to move the magnified viewport since the area around the
+ * moving finger will be magnified to fit the screen. For example, if the
+ * screen was not magnified and the user triple taps and holds the screen
+ * would magnify and the viewport will follow the user's finger. When the
+ * finger goes up the screen will clear zoom out. If the same user interaction
+ * is performed when the screen is magnified, the viewport movement will
+ * be the same but when the finger goes up the screen will stay magnified.
+ * In other words, the initial magnified state is sticky.
+ *
+ * 3. Pinching with any number of additional fingers when viewport dragging
+ * is enabled, i.e. the user triple tapped and holds, would adjust the
+ * magnification scale which will become the current default magnification
+ * scale. The next time the user magnifies the same magnification scale
+ * would be used.
+ *
+ * 4. When in a permanent magnified state the user can use two or more fingers
+ * to pan the viewport. Note that in this mode the content is panned as
+ * opposed to the viewport dragging mode in which the viewport is moved.
+ *
+ * 5. When in a permanent magnified state the user can use three or more
+ * fingers to change the magnification scale which will become the current
+ * default magnification scale. The next time the user magnifies the same
+ * magnification scale would be used.
+ *
+ * 6. The magnification scale will be persisted in settings and in the cloud.
+ */
+public final class ScreenMagnifier implements EventStreamTransformation {
+
+ private static final boolean DEBUG_STATE_TRANSITIONS = false;
+ private static final boolean DEBUG_DETECTING = false;
+ private static final boolean DEBUG_TRANSFORMATION = false;
+ private static final boolean DEBUG_PANNING = false;
+ private static final boolean DEBUG_SCALING = false;
+ private static final boolean DEBUG_VIEWPORT_WINDOW = false;
+ private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
+ private static final boolean DEBUG_ROTATION = false;
+ private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
+
+ private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
+
+ private static final int STATE_DELEGATING = 1;
+ private static final int STATE_DETECTING = 2;
+ private static final int STATE_VIEWPORT_DRAGGING = 3;
+ private static final int STATE_MAGNIFIED_INTERACTION = 4;
+
+ private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
+ private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1;
+ private static final float DEFAULT_WINDOW_ANIMATION_SCALE = 1.0f;
+
+ private static final int MULTI_TAP_TIME_SLOP_ADJUSTMENT = 50;
+
+ private final IWindowManager mWindowManagerService = IWindowManager.Stub.asInterface(
+ ServiceManager.getService("window"));
+ private final WindowManager mWindowManager;
+ private final DisplayProvider mDisplayProvider;
+
+ private final DetectingStateHandler mDetectingStateHandler = new DetectingStateHandler();
+ private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler;
+ private final StateViewportDraggingHandler mStateViewportDraggingHandler =
+ new StateViewportDraggingHandler();
+
+ private final Interpolator mInterpolator = new DecelerateInterpolator(2.5f);
+
+ private final MagnificationController mMagnificationController;
+ private final DisplayContentObserver mDisplayContentObserver;
+ private final ScreenStateObserver mScreenStateObserver;
+ private final Viewport mViewport;
+
+ private final int mTapTimeSlop = ViewConfiguration.getTapTimeout();
+ private final int mMultiTapTimeSlop =
+ ViewConfiguration.getDoubleTapTimeout() - MULTI_TAP_TIME_SLOP_ADJUSTMENT;
+ private final int mTapDistanceSlop;
+ private final int mMultiTapDistanceSlop;
+
+ private final int mShortAnimationDuration;
+ private final int mLongAnimationDuration;
+ private final float mWindowAnimationScale;
+
+ private final Context mContext;
+
+ private EventStreamTransformation mNext;
+
+ private int mCurrentState;
+ private int mPreviousState;
+ private boolean mTranslationEnabledBeforePan;
+
+ private PointerCoords[] mTempPointerCoords;
+ private PointerProperties[] mTempPointerProperties;
+
+ public ScreenMagnifier(Context context) {
+ mContext = context;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+
+ mShortAnimationDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_shortAnimTime);
+ mLongAnimationDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+ mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+ mWindowAnimationScale = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.WINDOW_ANIMATION_SCALE, DEFAULT_WINDOW_ANIMATION_SCALE);
+
+ mMagnificationController = new MagnificationController(mShortAnimationDuration);
+ mDisplayProvider = new DisplayProvider(context, mWindowManager);
+ mViewport = new Viewport(mContext, mWindowManager, mWindowManagerService,
+ mDisplayProvider, mInterpolator, mShortAnimationDuration);
+ mDisplayContentObserver = new DisplayContentObserver(mContext, mViewport,
+ mMagnificationController, mWindowManagerService, mDisplayProvider,
+ mLongAnimationDuration, mWindowAnimationScale);
+ mScreenStateObserver = new ScreenStateObserver(mContext, mViewport,
+ mMagnificationController);
+
+ mMagnifiedContentInteractonStateHandler = new MagnifiedContentInteractonStateHandler(
+ context);
+
+ transitionToState(STATE_DETECTING);
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ mMagnifiedContentInteractonStateHandler.onMotionEvent(event);
+ switch (mCurrentState) {
+ case STATE_DELEGATING: {
+ handleMotionEventStateDelegating(event, policyFlags);
+ } break;
+ case STATE_DETECTING: {
+ mDetectingStateHandler.onMotionEvent(event, policyFlags);
+ } break;
+ case STATE_VIEWPORT_DRAGGING: {
+ mStateViewportDraggingHandler.onMotionEvent(event, policyFlags);
+ } break;
+ case STATE_MAGNIFIED_INTERACTION: {
+ // mMagnifiedContentInteractonStateHandler handles events only
+ // if this is the current state since it uses ScaleGestureDetecotr
+ // and a GestureDetector which need well formed event stream.
+ } break;
+ default: {
+ throw new IllegalStateException("Unknown state: " + mCurrentState);
+ }
+ }
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
+ public void clear() {
+ mCurrentState = STATE_DETECTING;
+ mDetectingStateHandler.clear();
+ mStateViewportDraggingHandler.clear();
+ mMagnifiedContentInteractonStateHandler.clear();
+ if (mNext != null) {
+ mNext.clear();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ mMagnificationController.setScaleAndMagnifiedRegionCenter(1.0f,
+ 0, 0, true);
+ mViewport.setFrameShown(false, true);
+ mDisplayProvider.destroy();
+ mDisplayContentObserver.destroy();
+ mScreenStateObserver.destroy();
+ }
+
+ private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (mDetectingStateHandler.mDelayedEventQueue == null) {
+ transitionToState(STATE_DETECTING);
+ }
+ }
+ if (mNext != null) {
+ // If the event is within the magnified portion of the screen we have
+ // to change its location to be where the user thinks he is poking the
+ // UI which may have been magnified and panned.
+ final float eventX = event.getX();
+ final float eventY = event.getY();
+ if (mMagnificationController.isMagnifying()
+ && mViewport.getBounds().contains((int) eventX, (int) eventY)) {
+ final float scale = mMagnificationController.getScale();
+ final float scaledOffsetX = mMagnificationController.getScaledOffsetX();
+ final float scaledOffsetY = mMagnificationController.getScaledOffsetY();
+ final int pointerCount = event.getPointerCount();
+ PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
+ PointerProperties[] properties = getTempPointerPropertiesWithMinSize(pointerCount);
+ for (int i = 0; i < pointerCount; i++) {
+ event.getPointerCoords(i, coords[i]);
+ coords[i].x = (coords[i].x - scaledOffsetX) / scale;
+ coords[i].y = (coords[i].y - scaledOffsetY) / scale;
+ event.getPointerProperties(i, properties[i]);
+ }
+ event = MotionEvent.obtain(event.getDownTime(),
+ event.getEventTime(), event.getAction(), pointerCount, properties,
+ coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
+ event.getFlags());
+ }
+ mNext.onMotionEvent(event, policyFlags);
+ }
+ }
+
+ private PointerCoords[] getTempPointerCoordsWithMinSize(int size) {
+ final int oldSize = (mTempPointerCoords != null) ? mTempPointerCoords.length : 0;
+ if (oldSize < size) {
+ PointerCoords[] oldTempPointerCoords = mTempPointerCoords;
+ mTempPointerCoords = new PointerCoords[size];
+ if (oldTempPointerCoords != null) {
+ System.arraycopy(oldTempPointerCoords, 0, mTempPointerCoords, 0, oldSize);
+ }
+ }
+ for (int i = oldSize; i < size; i++) {
+ mTempPointerCoords[i] = new PointerCoords();
+ }
+ return mTempPointerCoords;
+ }
+
+ private PointerProperties[] getTempPointerPropertiesWithMinSize(int size) {
+ final int oldSize = (mTempPointerProperties != null) ? mTempPointerProperties.length : 0;
+ if (oldSize < size) {
+ PointerProperties[] oldTempPointerProperties = mTempPointerProperties;
+ mTempPointerProperties = new PointerProperties[size];
+ if (oldTempPointerProperties != null) {
+ System.arraycopy(oldTempPointerProperties, 0, mTempPointerProperties, 0, oldSize);
+ }
+ }
+ for (int i = oldSize; i < size; i++) {
+ mTempPointerProperties[i] = new PointerProperties();
+ }
+ return mTempPointerProperties;
+ }
+
+ private void transitionToState(int state) {
+ if (DEBUG_STATE_TRANSITIONS) {
+ switch (state) {
+ case STATE_DELEGATING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_DELEGATING");
+ } break;
+ case STATE_DETECTING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_DETECTING");
+ } break;
+ case STATE_VIEWPORT_DRAGGING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_VIEWPORT_DRAGGING");
+ } break;
+ case STATE_MAGNIFIED_INTERACTION: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_MAGNIFIED_INTERACTION");
+ } break;
+ default: {
+ throw new IllegalArgumentException("Unknown state: " + state);
+ }
+ }
+ }
+ mPreviousState = mCurrentState;
+ mCurrentState = state;
+ }
+
+ private final class MagnifiedContentInteractonStateHandler
+ extends SimpleOnGestureListener implements OnScaleGestureListener {
+ private static final float MIN_SCALE = 1.3f;
+ private static final float MAX_SCALE = 5.0f;
+
+ private static final float SCALING_THRESHOLD = 0.3f;
+
+ private final ScaleGestureDetector mScaleGestureDetector;
+ private final GestureDetector mGestureDetector;
+
+ private float mInitialScaleFactor = -1;
+ private boolean mScaling;
+
+ public MagnifiedContentInteractonStateHandler(Context context) {
+ mScaleGestureDetector = new ScaleGestureDetector(context, this);
+ mGestureDetector = new GestureDetector(context, this);
+ }
+
+ public void onMotionEvent(MotionEvent event) {
+ mScaleGestureDetector.onTouchEvent(event);
+ mGestureDetector.onTouchEvent(event);
+ if (mCurrentState != STATE_MAGNIFIED_INTERACTION) {
+ return;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ clear();
+ final float scale = mMagnificationController.getScale();
+ if (scale != getPersistedScale()) {
+ persistScale(scale);
+ }
+ if (mPreviousState == STATE_VIEWPORT_DRAGGING) {
+ transitionToState(STATE_VIEWPORT_DRAGGING);
+ } else {
+ transitionToState(STATE_DETECTING);
+ }
+ }
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent first, MotionEvent second, float distanceX,
+ float distanceY) {
+ if (mCurrentState != STATE_MAGNIFIED_INTERACTION) {
+ return true;
+ }
+ final float scale = mMagnificationController.getScale();
+ final float scrollX = distanceX / scale;
+ final float scrollY = distanceY / scale;
+ final float centerX = mMagnificationController.getMagnifiedRegionCenterX() + scrollX;
+ final float centerY = mMagnificationController.getMagnifiedRegionCenterY() + scrollY;
+ if (DEBUG_PANNING) {
+ Slog.i(LOG_TAG, "Panned content by scrollX: " + scrollX
+ + " scrollY: " + scrollY);
+ }
+ mMagnificationController.setMagnifiedRegionCenter(centerX, centerY, false);
+ return true;
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ if (!mScaling) {
+ if (mInitialScaleFactor < 0) {
+ mInitialScaleFactor = detector.getScaleFactor();
+ } else {
+ final float deltaScale = detector.getScaleFactor() - mInitialScaleFactor;
+ if (Math.abs(deltaScale) > SCALING_THRESHOLD) {
+ mScaling = true;
+ return true;
+ }
+ }
+ return false;
+ }
+ final float newScale = mMagnificationController.getScale()
+ * detector.getScaleFactor();
+ final float normalizedNewScale = Math.min(Math.max(newScale, MIN_SCALE), MAX_SCALE);
+ if (DEBUG_SCALING) {
+ Slog.i(LOG_TAG, "normalizedNewScale: " + normalizedNewScale);
+ }
+ mMagnificationController.setScale(normalizedNewScale, detector.getFocusX(),
+ detector.getFocusY(), false);
+ return true;
+ }
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return (mCurrentState == STATE_MAGNIFIED_INTERACTION);
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ clear();
+ }
+
+ private void clear() {
+ mInitialScaleFactor = -1;
+ mScaling = false;
+ }
+ }
+
+ private final class StateViewportDraggingHandler {
+ private boolean mLastMoveOutsideMagnifiedRegion;
+
+ private void onMotionEvent(MotionEvent event, int policyFlags) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ throw new IllegalArgumentException("Unexpected event type: ACTION_DOWN");
+ }
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ clear();
+ transitionToState(STATE_MAGNIFIED_INTERACTION);
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ if (event.getPointerCount() != 1) {
+ throw new IllegalStateException("Should have one pointer down.");
+ }
+ final float eventX = event.getX();
+ final float eventY = event.getY();
+ if (mViewport.getBounds().contains((int) eventX, (int) eventY)) {
+ if (mLastMoveOutsideMagnifiedRegion) {
+ mLastMoveOutsideMagnifiedRegion = false;
+ mMagnificationController.setMagnifiedRegionCenter(eventX,
+ eventY, true);
+ } else {
+ mMagnificationController.setMagnifiedRegionCenter(eventX,
+ eventY, false);
+ }
+ } else {
+ mLastMoveOutsideMagnifiedRegion = true;
+ }
+ } break;
+ case MotionEvent.ACTION_UP: {
+ if (!mTranslationEnabledBeforePan) {
+ mMagnificationController.reset(true);
+ mViewport.setFrameShown(false, true);
+ }
+ clear();
+ transitionToState(STATE_DETECTING);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ throw new IllegalArgumentException("Unexpected event type: ACTION_POINTER_UP");
+ }
+ }
+ }
+
+ public void clear() {
+ mLastMoveOutsideMagnifiedRegion = false;
+ }
+ }
+
+ private final class DetectingStateHandler {
+
+ private static final int MESSAGE_ON_ACTION_TAP_AND_HOLD = 1;
+
+ private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
+
+ private static final int ACTION_TAP_COUNT = 3;
+
+ private MotionEventInfo mDelayedEventQueue;
+
+ private MotionEvent mLastDownEvent;
+ private MotionEvent mLastTapUpEvent;
+ private int mTapCount;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ final int type = message.what;
+ switch (type) {
+ case MESSAGE_ON_ACTION_TAP_AND_HOLD: {
+ MotionEvent event = (MotionEvent) message.obj;
+ final int policyFlags = message.arg1;
+ onActionTapAndHold(event, policyFlags);
+ } break;
+ case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
+ transitionToState(STATE_DELEGATING);
+ sendDelayedMotionEvents();
+ clear();
+ } break;
+ default: {
+ throw new IllegalArgumentException("Unknown message type: " + type);
+ }
+ }
+ }
+ };
+
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ cacheDelayedMotionEvent(event, policyFlags);
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ if (!mViewport.getBounds().contains((int) event.getX(),
+ (int) event.getY())) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ if (mTapCount == ACTION_TAP_COUNT - 1 && mLastDownEvent != null
+ && GestureUtils.isMultiTap(mLastDownEvent, event,
+ mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+ Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
+ policyFlags, 0, event);
+ mHandler.sendMessageDelayed(message,
+ ViewConfiguration.getLongPressTimeout());
+ } else if (mTapCount < ACTION_TAP_COUNT) {
+ Message message = mHandler.obtainMessage(
+ MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ mHandler.sendMessageDelayed(message, mMultiTapTimeSlop);
+ }
+ clearLastDownEvent();
+ mLastDownEvent = MotionEvent.obtain(event);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ if (mMagnificationController.isMagnifying()) {
+ transitionToState(STATE_MAGNIFIED_INTERACTION);
+ clear();
+ } else {
+ transitionToDelegatingStateAndClear();
+ }
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ if (mLastDownEvent != null && mTapCount < ACTION_TAP_COUNT - 1) {
+ final double distance = GestureUtils.computeDistance(mLastDownEvent,
+ event, 0);
+ if (Math.abs(distance) > mTapDistanceSlop) {
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ } break;
+ case MotionEvent.ACTION_UP: {
+ if (mLastDownEvent == null) {
+ return;
+ }
+ mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
+ if (!mViewport.getBounds().contains((int) event.getX(), (int) event.getY())) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop,
+ mTapDistanceSlop, 0)) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(mLastTapUpEvent,
+ event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ mTapCount++;
+ if (DEBUG_DETECTING) {
+ Slog.i(LOG_TAG, "Tap count:" + mTapCount);
+ }
+ if (mTapCount == ACTION_TAP_COUNT) {
+ clear();
+ onActionTap(event, policyFlags);
+ return;
+ }
+ clearLastTapUpEvent();
+ mLastTapUpEvent = MotionEvent.obtain(event);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ /* do nothing */
+ } break;
+ }
+ }
+
+ public void clear() {
+ mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ clearTapDetectionState();
+ clearDelayedMotionEvents();
+ }
+
+ private void clearTapDetectionState() {
+ mTapCount = 0;
+ clearLastTapUpEvent();
+ clearLastDownEvent();
+ }
+
+ private void clearLastTapUpEvent() {
+ if (mLastTapUpEvent != null) {
+ mLastTapUpEvent.recycle();
+ mLastTapUpEvent = null;
+ }
+ }
+
+ private void clearLastDownEvent() {
+ if (mLastDownEvent != null) {
+ mLastDownEvent.recycle();
+ mLastDownEvent = null;
+ }
+ }
+
+ private void cacheDelayedMotionEvent(MotionEvent event, int policyFlags) {
+ MotionEventInfo info = MotionEventInfo.obtain(event, policyFlags);
+ if (mDelayedEventQueue == null) {
+ mDelayedEventQueue = info;
+ } else {
+ MotionEventInfo tail = mDelayedEventQueue;
+ while (tail.mNext != null) {
+ tail = tail.mNext;
+ }
+ tail.mNext = info;
+ }
+ }
+
+ private void sendDelayedMotionEvents() {
+ while (mDelayedEventQueue != null) {
+ MotionEventInfo info = mDelayedEventQueue;
+ mDelayedEventQueue = info.mNext;
+ ScreenMagnifier.this.onMotionEvent(info.mEvent, info.mPolicyFlags);
+ info.recycle();
+ }
+ }
+
+ private void clearDelayedMotionEvents() {
+ while (mDelayedEventQueue != null) {
+ MotionEventInfo info = mDelayedEventQueue;
+ mDelayedEventQueue = info.mNext;
+ info.recycle();
+ }
+ }
+
+ private void transitionToDelegatingStateAndClear() {
+ transitionToState(STATE_DELEGATING);
+ sendDelayedMotionEvents();
+ clear();
+ }
+
+ private void onActionTap(MotionEvent up, int policyFlags) {
+ if (DEBUG_DETECTING) {
+ Slog.i(LOG_TAG, "onActionTap()");
+ }
+ if (!mMagnificationController.isMagnifying()) {
+ mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
+ up.getX(), up.getY(), true);
+ mViewport.setFrameShown(true, true);
+ } else {
+ mMagnificationController.reset(true);
+ mViewport.setFrameShown(false, true);
+ }
+ }
+
+ private void onActionTapAndHold(MotionEvent down, int policyFlags) {
+ if (DEBUG_DETECTING) {
+ Slog.i(LOG_TAG, "onActionTapAndHold()");
+ }
+ clear();
+ mTranslationEnabledBeforePan = mMagnificationController.isMagnifying();
+ mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
+ down.getX(), down.getY(), true);
+ mViewport.setFrameShown(true, true);
+ transitionToState(STATE_VIEWPORT_DRAGGING);
+ }
+ }
+
+ private void persistScale(final float scale) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale);
+ return null;
+ }
+ }.execute();
+ }
+
+ private float getPersistedScale() {
+ return Settings.Secure.getFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ DEFAULT_MAGNIFICATION_SCALE);
+ }
+
+ private static boolean isScreenMagnificationAutoUpdateEnabled(Context context) {
+ return (Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+ DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE) == 1);
+ }
+
+ private static final class MotionEventInfo {
+
+ private static final int MAX_POOL_SIZE = 10;
+
+ private static final Object sLock = new Object();
+ private static MotionEventInfo sPool;
+ private static int sPoolSize;
+
+ private MotionEventInfo mNext;
+ private boolean mInPool;
+
+ public MotionEvent mEvent;
+ public int mPolicyFlags;
+
+ public static MotionEventInfo obtain(MotionEvent event, int policyFlags) {
+ synchronized (sLock) {
+ MotionEventInfo info;
+ if (sPoolSize > 0) {
+ sPoolSize--;
+ info = sPool;
+ sPool = info.mNext;
+ info.mNext = null;
+ info.mInPool = false;
+ } else {
+ info = new MotionEventInfo();
+ }
+ info.initialize(event, policyFlags);
+ return info;
+ }
+ }
+
+ private void initialize(MotionEvent event, int policyFlags) {
+ mEvent = MotionEvent.obtain(event);
+ mPolicyFlags = policyFlags;
+ }
+
+ public void recycle() {
+ synchronized (sLock) {
+ if (mInPool) {
+ throw new IllegalStateException("Already recycled.");
+ }
+ clear();
+ if (sPoolSize < MAX_POOL_SIZE) {
+ sPoolSize++;
+ mNext = sPool;
+ sPool = this;
+ mInPool = true;
+ }
+ }
+ }
+
+ private void clear() {
+ mEvent.recycle();
+ mEvent = null;
+ mPolicyFlags = 0;
+ }
+ }
+
+ private static final class ScreenStateObserver extends BroadcastReceiver {
+
+ private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_ON_SCREEN_STATE_CHANGE: {
+ String action = (String) message.obj;
+ handleOnScreenStateChange(action);
+ } break;
+ }
+ }
+ };
+
+ private final Context mContext;
+ private final Viewport mViewport;
+ private final MagnificationController mMagnificationController;
+
+ public ScreenStateObserver(Context context, Viewport viewport,
+ MagnificationController magnificationController) {
+ mContext = context;
+ mViewport = viewport;
+ mMagnificationController = magnificationController;
+ mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ }
+
+ public void destroy() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(MESSAGE_ON_SCREEN_STATE_CHANGE,
+ intent.getAction()).sendToTarget();
+ }
+
+ private void handleOnScreenStateChange(String action) {
+ if (action.equals(Intent.ACTION_SCREEN_OFF)
+ && mMagnificationController.isMagnifying()
+ && isScreenMagnificationAutoUpdateEnabled(mContext)) {
+ mMagnificationController.reset(false);
+ mViewport.setFrameShown(false, false);
+ }
+ }
+ }
+
+ private static final class DisplayContentObserver {
+
+ private static final int MESSAGE_SHOW_VIEWPORT_FRAME = 1;
+ private static final int MESSAGE_RECOMPUTE_VIEWPORT_BOUNDS = 2;
+ private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 3;
+ private static final int MESSAGE_ON_WINDOW_TRANSITION = 4;
+ private static final int MESSAGE_ON_ROTATION_CHANGED = 5;
+
+ private final Handler mHandler = new MyHandler();
+
+ private final Rect mTempRect = new Rect();
+
+ private final IDisplayContentChangeListener mDisplayContentChangeListener;
+
+ private final Context mContext;
+ private final Viewport mViewport;
+ private final MagnificationController mMagnificationController;
+ private final IWindowManager mWindowManagerService;
+ private final DisplayProvider mDisplayProvider;
+ private final long mLongAnimationDuration;
+ private final float mWindowAnimationScale;
+
+ public DisplayContentObserver(Context context, Viewport viewport,
+ MagnificationController magnificationController,
+ IWindowManager windowManagerService, DisplayProvider displayProvider,
+ long longAnimationDuration, float windowAnimationScale) {
+ mContext = context;
+ mViewport = viewport;
+ mMagnificationController = magnificationController;
+ mWindowManagerService = windowManagerService;
+ mDisplayProvider = displayProvider;
+ mLongAnimationDuration = longAnimationDuration;
+ mWindowAnimationScale = windowAnimationScale;
+
+ mDisplayContentChangeListener = new IDisplayContentChangeListener.Stub() {
+ @Override
+ public void onWindowTransition(int displayId, int transition, WindowInfo info) {
+ mHandler.obtainMessage(MESSAGE_ON_WINDOW_TRANSITION, transition, 0,
+ WindowInfo.obtain(info)).sendToTarget();
+ }
+
+ @Override
+ public void onRectangleOnScreenRequested(int dsiplayId, Rect rectangle,
+ boolean immediate) {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = rectangle.left;
+ args.argi2 = rectangle.top;
+ args.argi3 = rectangle.right;
+ args.argi4 = rectangle.bottom;
+ mHandler.obtainMessage(MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED, 0,
+ immediate ? 1 : 0, args).sendToTarget();
+ }
+
+ @Override
+ public void onRotationChanged(int rotation) throws RemoteException {
+ mHandler.obtainMessage(MESSAGE_ON_ROTATION_CHANGED, rotation, 0)
+ .sendToTarget();
+ }
+ };
+
+ try {
+ mWindowManagerService.addDisplayContentChangeListener(
+ mDisplayProvider.getDisplay().getDisplayId(),
+ mDisplayContentChangeListener);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ public void destroy() {
+ try {
+ mWindowManagerService.removeDisplayContentChangeListener(
+ mDisplayProvider.getDisplay().getDisplayId(),
+ mDisplayContentChangeListener);
+ } catch (RemoteException re) {
+ /* ignore*/
+ }
+ }
+
+ private void handleOnRotationChanged(int rotation) {
+ if (DEBUG_ROTATION) {
+ Slog.i(LOG_TAG, "Rotation: " + rotationToString(rotation));
+ }
+ resetMagnificationIfNeeded();
+ mViewport.setFrameShown(false, false);
+ mViewport.rotationChanged();
+ mViewport.recomputeBounds(false);
+ if (mMagnificationController.isMagnifying()) {
+ final long delay = (long) (2 * mLongAnimationDuration * mWindowAnimationScale);
+ Message message = mHandler.obtainMessage(MESSAGE_SHOW_VIEWPORT_FRAME);
+ mHandler.sendMessageDelayed(message, delay);
+ }
+ }
+
+ private void handleOnWindowTransition(int transition, WindowInfo info) {
+ if (DEBUG_WINDOW_TRANSITIONS) {
+ Slog.i(LOG_TAG, "Window transitioning: "
+ + windowTransitionToString(transition));
+ }
+ try {
+ final boolean magnifying = mMagnificationController.isMagnifying();
+ if (magnifying) {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN:
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE:
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ resetMagnificationIfNeeded();
+ }
+ }
+ }
+ if (info.type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+ || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
+ || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG
+ || info.type == WindowManager.LayoutParams.TYPE_KEYGUARD
+ || info.type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ case WindowManagerPolicy.TRANSIT_SHOW:
+ case WindowManagerPolicy.TRANSIT_EXIT:
+ case WindowManagerPolicy.TRANSIT_HIDE: {
+ mViewport.recomputeBounds(mMagnificationController.isMagnifying());
+ } break;
+ }
+ } else {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ case WindowManagerPolicy.TRANSIT_SHOW: {
+ if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) {
+ break;
+ }
+ final int type = info.type;
+ switch (type) {
+ // TODO: Are these all the windows we want to make
+ // visible when they appear on the screen?
+ // Do we need to take some of them out?
+ case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+ case WindowManager.LayoutParams.TYPE_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+ case WindowManager.LayoutParams.TYPE_TOAST:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+ case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+ case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
+ Rect magnifiedRegionBounds = mMagnificationController
+ .getMagnifiedRegionBounds();
+ Rect touchableRegion = info.touchableRegion;
+ if (!magnifiedRegionBounds.intersect(touchableRegion)) {
+ ensureRectangleInMagnifiedRegionBounds(
+ magnifiedRegionBounds, touchableRegion);
+ }
+ } break;
+ } break;
+ }
+ }
+ }
+ } finally {
+ if (info != null) {
+ info.recycle();
+ }
+ }
+ }
+
+ private void handleOnRectangleOnScreenRequested(Rect rectangle, boolean immediate) {
+ if (!mMagnificationController.isMagnifying()) {
+ return;
+ }
+ Rect magnifiedRegionBounds = mMagnificationController.getMagnifiedRegionBounds();
+ if (magnifiedRegionBounds.contains(rectangle)) {
+ return;
+ }
+ ensureRectangleInMagnifiedRegionBounds(magnifiedRegionBounds, rectangle);
+ }
+
+ private void ensureRectangleInMagnifiedRegionBounds(Rect magnifiedRegionBounds,
+ Rect rectangle) {
+ if (!Rect.intersects(rectangle, mViewport.getBounds())) {
+ return;
+ }
+ final float scrollX;
+ final float scrollY;
+ if (rectangle.width() > magnifiedRegionBounds.width()) {
+ scrollX = rectangle.left - magnifiedRegionBounds.left;
+ } else if (rectangle.left < magnifiedRegionBounds.left) {
+ scrollX = rectangle.left - magnifiedRegionBounds.left;
+ } else if (rectangle.right > magnifiedRegionBounds.right) {
+ scrollX = rectangle.right - magnifiedRegionBounds.right;
+ } else {
+ scrollX = 0;
+ }
+ if (rectangle.height() > magnifiedRegionBounds.height()) {
+ scrollY = rectangle.top - magnifiedRegionBounds.top;
+ } else if (rectangle.top < magnifiedRegionBounds.top) {
+ scrollY = rectangle.top - magnifiedRegionBounds.top;
+ } else if (rectangle.bottom > magnifiedRegionBounds.bottom) {
+ scrollY = rectangle.bottom - magnifiedRegionBounds.bottom;
+ } else {
+ scrollY = 0;
+ }
+ final float viewportCenterX = mMagnificationController.getMagnifiedRegionCenterX()
+ + scrollX;
+ final float viewportCenterY = mMagnificationController.getMagnifiedRegionCenterY()
+ + scrollY;
+ mMagnificationController.setMagnifiedRegionCenter(viewportCenterX, viewportCenterY,
+ true);
+ }
+
+ private void resetMagnificationIfNeeded() {
+ if (mMagnificationController.isMagnifying()
+ && isScreenMagnificationAutoUpdateEnabled(mContext)) {
+ mMagnificationController.reset(true);
+ mViewport.setFrameShown(false, true);
+ }
+ }
+
+ private String windowTransitionToString(int transition) {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_UNSET: {
+ return "TRANSIT_UNSET";
+ }
+ case WindowManagerPolicy.TRANSIT_NONE: {
+ return "TRANSIT_NONE";
+ }
+ case WindowManagerPolicy.TRANSIT_ENTER: {
+ return "TRANSIT_ENTER";
+ }
+ case WindowManagerPolicy.TRANSIT_EXIT: {
+ return "TRANSIT_EXIT";
+ }
+ case WindowManagerPolicy.TRANSIT_SHOW: {
+ return "TRANSIT_SHOW";
+ }
+ case WindowManagerPolicy.TRANSIT_EXIT_MASK: {
+ return "TRANSIT_EXIT_MASK";
+ }
+ case WindowManagerPolicy.TRANSIT_PREVIEW_DONE: {
+ return "TRANSIT_PREVIEW_DONE";
+ }
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: {
+ return "TRANSIT_ACTIVITY_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: {
+ return "TRANSIT_ACTIVITY_CLOSE";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN: {
+ return "TRANSIT_TASK_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_CLOSE: {
+ return "TRANSIT_TASK_CLOSE";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT: {
+ return "TRANSIT_TASK_TO_FRONT";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_TO_BACK: {
+ return "TRANSIT_TASK_TO_BACK";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE: {
+ return "TRANSIT_WALLPAPER_CLOSE";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN: {
+ return "TRANSIT_WALLPAPER_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ return "TRANSIT_WALLPAPER_INTRA_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE: {
+ return "TRANSIT_WALLPAPER_INTRA_CLOSE";
+ }
+ default: {
+ return "<UNKNOWN>";
+ }
+ }
+ }
+
+ private String rotationToString(int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0: {
+ return "ROTATION_0";
+ }
+ case Surface.ROTATION_90: {
+ return "ROATATION_90";
+ }
+ case Surface.ROTATION_180: {
+ return "ROATATION_180";
+ }
+ case Surface.ROTATION_270: {
+ return "ROATATION_270";
+ }
+ default: {
+ throw new IllegalArgumentException("Invalid rotation: "
+ + rotation);
+ }
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ @Override
+ public void handleMessage(Message message) {
+ final int action = message.what;
+ switch (action) {
+ case MESSAGE_SHOW_VIEWPORT_FRAME: {
+ mViewport.setFrameShown(true, true);
+ } break;
+ case MESSAGE_RECOMPUTE_VIEWPORT_BOUNDS: {
+ final boolean animate = message.arg1 == 1;
+ mViewport.recomputeBounds(animate);
+ } break;
+ case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ try {
+ mTempRect.set(args.argi1, args.argi2, args.argi3, args.argi4);
+ final boolean immediate = (message.arg1 == 1);
+ handleOnRectangleOnScreenRequested(mTempRect, immediate);
+ } finally {
+ args.recycle();
+ }
+ } break;
+ case MESSAGE_ON_WINDOW_TRANSITION: {
+ final int transition = message.arg1;
+ WindowInfo info = (WindowInfo) message.obj;
+ handleOnWindowTransition(transition, info);
+ } break;
+ case MESSAGE_ON_ROTATION_CHANGED: {
+ final int rotation = message.arg1;
+ handleOnRotationChanged(rotation);
+ } break;
+ default: {
+ throw new IllegalArgumentException("Unknown message: " + action);
+ }
+ }
+ }
+ }
+ }
+
+ private final class MagnificationController {
+
+ private static final String PROPERTY_NAME_ACCESSIBILITY_TRANSFORMATION =
+ "accessibilityTransformation";
+
+ private final MagnificationSpec mSentMagnificationSpec = new MagnificationSpec();
+
+ private final MagnificationSpec mCurrentMagnificationSpec = new MagnificationSpec();
+
+ private final Rect mTempRect = new Rect();
+
+ private final ValueAnimator mTransformationAnimator;
+
+ public MagnificationController(int animationDuration) {
+ Property<MagnificationController, MagnificationSpec> property =
+ Property.of(MagnificationController.class, MagnificationSpec.class,
+ PROPERTY_NAME_ACCESSIBILITY_TRANSFORMATION);
+ TypeEvaluator<MagnificationSpec> evaluator = new TypeEvaluator<MagnificationSpec>() {
+ private final MagnificationSpec mTempTransformationSpec = new MagnificationSpec();
+ @Override
+ public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ MagnificationSpec result = mTempTransformationSpec;
+ result.mScale = fromSpec.mScale
+ + (toSpec.mScale - fromSpec.mScale) * fraction;
+ result.mMagnifiedRegionCenterX = fromSpec.mMagnifiedRegionCenterX
+ + (toSpec.mMagnifiedRegionCenterX - fromSpec.mMagnifiedRegionCenterX)
+ * fraction;
+ result.mMagnifiedRegionCenterY = fromSpec.mMagnifiedRegionCenterY
+ + (toSpec.mMagnifiedRegionCenterY - fromSpec.mMagnifiedRegionCenterY)
+ * fraction;
+ result.mScaledOffsetX = fromSpec.mScaledOffsetX
+ + (toSpec.mScaledOffsetX - fromSpec.mScaledOffsetX)
+ * fraction;
+ result.mScaledOffsetY = fromSpec.mScaledOffsetY
+ + (toSpec.mScaledOffsetY - fromSpec.mScaledOffsetY)
+ * fraction;
+ return result;
+ }
+ };
+ mTransformationAnimator = ObjectAnimator.ofObject(this, property,
+ evaluator, mSentMagnificationSpec, mCurrentMagnificationSpec);
+ mTransformationAnimator.setDuration((long) (animationDuration));
+ mTransformationAnimator.setInterpolator(mInterpolator);
+ }
+
+ public boolean isMagnifying() {
+ return mCurrentMagnificationSpec.mScale > 1.0f;
+ }
+
+ public void reset(boolean animate) {
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ mCurrentMagnificationSpec.reset();
+ if (animate) {
+ animateAccessibilityTranformation(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setAccessibilityTransformation(mCurrentMagnificationSpec);
+ }
+ }
+
+ public Rect getMagnifiedRegionBounds() {
+ mTempRect.set(mViewport.getBounds());
+ mTempRect.offset((int) -mCurrentMagnificationSpec.mScaledOffsetX,
+ (int) -mCurrentMagnificationSpec.mScaledOffsetY);
+ mTempRect.scale(1.0f / mCurrentMagnificationSpec.mScale);
+ return mTempRect;
+ }
+
+ public float getScale() {
+ return mCurrentMagnificationSpec.mScale;
+ }
+
+ public float getMagnifiedRegionCenterX() {
+ return mCurrentMagnificationSpec.mMagnifiedRegionCenterX;
+ }
+
+ public float getMagnifiedRegionCenterY() {
+ return mCurrentMagnificationSpec.mMagnifiedRegionCenterY;
+ }
+
+ public float getScaledOffsetX() {
+ return mCurrentMagnificationSpec.mScaledOffsetX;
+ }
+
+ public float getScaledOffsetY() {
+ return mCurrentMagnificationSpec.mScaledOffsetY;
+ }
+
+ public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
+ MagnificationSpec spec = mCurrentMagnificationSpec;
+ final float oldScale = spec.mScale;
+ final float oldCenterX = spec.mMagnifiedRegionCenterX;
+ final float oldCenterY = spec.mMagnifiedRegionCenterY;
+ final float normPivotX = (-spec.mScaledOffsetX + pivotX) / oldScale;
+ final float normPivotY = (-spec.mScaledOffsetY + pivotY) / oldScale;
+ final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+ final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+ final float centerX = normPivotX + offsetX;
+ final float centerY = normPivotY + offsetY;
+ setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, animate);
+ }
+
+ public void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) {
+ setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.mScale, centerX, centerY,
+ animate);
+ }
+
+ public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY,
+ boolean animate) {
+ if (Float.compare(mCurrentMagnificationSpec.mScale, scale) == 0
+ && Float.compare(mCurrentMagnificationSpec.mMagnifiedRegionCenterX,
+ centerX) == 0
+ && Float.compare(mCurrentMagnificationSpec.mMagnifiedRegionCenterY,
+ centerY) == 0) {
+ return;
+ }
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ if (DEBUG_MAGNIFICATION_CONTROLLER) {
+ Slog.i(LOG_TAG, "scale: " + scale + " centerX: " + centerX
+ + " centerY: " + centerY);
+ }
+ mCurrentMagnificationSpec.initialize(scale, centerX, centerY);
+ if (animate) {
+ animateAccessibilityTranformation(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setAccessibilityTransformation(mCurrentMagnificationSpec);
+ }
+ }
+
+ private void animateAccessibilityTranformation(MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ mTransformationAnimator.setObjectValues(fromSpec, toSpec);
+ mTransformationAnimator.start();
+ }
+
+ @SuppressWarnings("unused")
+ // Called from an animator.
+ public MagnificationSpec getAccessibilityTransformation() {
+ return mSentMagnificationSpec;
+ }
+
+ public void setAccessibilityTransformation(MagnificationSpec transformation) {
+ if (DEBUG_TRANSFORMATION) {
+ Slog.i(LOG_TAG, "Transformation scale: " + transformation.mScale
+ + " offsetX: " + transformation.mScaledOffsetX
+ + " offsetY: " + transformation.mScaledOffsetY);
+ }
+ try {
+ mSentMagnificationSpec.updateFrom(transformation);
+ mWindowManagerService.magnifyDisplay(mDisplayProvider.getDisplay().getDisplayId(),
+ transformation.mScale, transformation.mScaledOffsetX,
+ transformation.mScaledOffsetY);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ private class MagnificationSpec {
+
+ private static final float DEFAULT_SCALE = 1.0f;
+
+ public float mScale = DEFAULT_SCALE;
+
+ public float mMagnifiedRegionCenterX;
+
+ public float mMagnifiedRegionCenterY;
+
+ public float mScaledOffsetX;
+
+ public float mScaledOffsetY;
+
+ public void initialize(float scale, float magnifiedRegionCenterX,
+ float magnifiedRegionCenterY) {
+ mScale = scale;
+
+ final int viewportWidth = mViewport.getBounds().width();
+ final int viewportHeight = mViewport.getBounds().height();
+ final float minMagnifiedRegionCenterX = (viewportWidth / 2) / scale;
+ final float minMagnifiedRegionCenterY = (viewportHeight / 2) / scale;
+ final float maxMagnifiedRegionCenterX = viewportWidth - minMagnifiedRegionCenterX;
+ final float maxMagnifiedRegionCenterY = viewportHeight - minMagnifiedRegionCenterY;
+
+ mMagnifiedRegionCenterX = Math.min(Math.max(magnifiedRegionCenterX,
+ minMagnifiedRegionCenterX), maxMagnifiedRegionCenterX);
+ mMagnifiedRegionCenterY = Math.min(Math.max(magnifiedRegionCenterY,
+ minMagnifiedRegionCenterY), maxMagnifiedRegionCenterY);
+
+ mScaledOffsetX = -(mMagnifiedRegionCenterX * scale - viewportWidth / 2);
+ mScaledOffsetY = -(mMagnifiedRegionCenterY * scale - viewportHeight / 2);
+ }
+
+ public void updateFrom(MagnificationSpec other) {
+ mScale = other.mScale;
+ mMagnifiedRegionCenterX = other.mMagnifiedRegionCenterX;
+ mMagnifiedRegionCenterY = other.mMagnifiedRegionCenterY;
+ mScaledOffsetX = other.mScaledOffsetX;
+ mScaledOffsetY = other.mScaledOffsetY;
+ }
+
+ public void reset() {
+ mScale = DEFAULT_SCALE;
+ mMagnifiedRegionCenterX = 0;
+ mMagnifiedRegionCenterY = 0;
+ mScaledOffsetX = 0;
+ mScaledOffsetY = 0;
+ }
+ }
+ }
+
+ private static final class Viewport {
+
+ private static final String PROPERTY_NAME_ALPHA = "alpha";
+
+ private static final String PROPERTY_NAME_BOUNDS = "bounds";
+
+ private static final int MIN_ALPHA = 0;
+
+ private static final int MAX_ALPHA = 255;
+
+ private final ArrayList<WindowInfo> mTempWindowInfoList = new ArrayList<WindowInfo>();
+
+ private final Rect mTempRect1 = new Rect();
+ private final Rect mTempRect2 = new Rect();
+ private final Rect mTempRect3 = new Rect();
+
+ private final IWindowManager mWindowManagerService;
+ private final DisplayProvider mDisplayProvider;
+
+ private final ViewportWindow mViewportFrame;
+
+ private final ValueAnimator mResizeFrameAnimator;
+
+ private final ValueAnimator mShowHideFrameAnimator;
+
+ public Viewport(Context context, WindowManager windowManager,
+ IWindowManager windowManagerService, DisplayProvider displayInfoProvider,
+ Interpolator animationInterpolator, long animationDuration) {
+ mWindowManagerService = windowManagerService;
+ mDisplayProvider = displayInfoProvider;
+ mViewportFrame = new ViewportWindow(context, windowManager, displayInfoProvider);
+
+ mShowHideFrameAnimator = ObjectAnimator.ofInt(mViewportFrame, PROPERTY_NAME_ALPHA,
+ MIN_ALPHA, MAX_ALPHA);
+ mShowHideFrameAnimator.setInterpolator(animationInterpolator);
+ mShowHideFrameAnimator.setDuration(animationDuration);
+ mShowHideFrameAnimator.addListener(new AnimatorListener() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mShowHideFrameAnimator.getAnimatedValue().equals(MIN_ALPHA)) {
+ mViewportFrame.hide();
+ }
+ }
+ @Override
+ public void onAnimationStart(Animator animation) {
+ /* do nothing - stub */
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ /* do nothing - stub */
+ }
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ /* do nothing - stub */
+ }
+ });
+
+ Property<ViewportWindow, Rect> property = Property.of(ViewportWindow.class,
+ Rect.class, PROPERTY_NAME_BOUNDS);
+ TypeEvaluator<Rect> evaluator = new TypeEvaluator<Rect>() {
+ private final Rect mReusableResultRect = new Rect();
+ @Override
+ public Rect evaluate(float fraction, Rect fromFrame, Rect toFrame) {
+ Rect result = mReusableResultRect;
+ result.left = (int) (fromFrame.left
+ + (toFrame.left - fromFrame.left) * fraction);
+ result.top = (int) (fromFrame.top
+ + (toFrame.top - fromFrame.top) * fraction);
+ result.right = (int) (fromFrame.right
+ + (toFrame.right - fromFrame.right) * fraction);
+ result.bottom = (int) (fromFrame.bottom
+ + (toFrame.bottom - fromFrame.bottom) * fraction);
+ return result;
+ }
+ };
+ mResizeFrameAnimator = ObjectAnimator.ofObject(mViewportFrame, property,
+ evaluator, mViewportFrame.mBounds, mViewportFrame.mBounds);
+ mResizeFrameAnimator.setDuration((long) (animationDuration));
+ mResizeFrameAnimator.setInterpolator(animationInterpolator);
+
+ recomputeBounds(false);
+ }
+
+ private final Comparator<WindowInfo> mWindowInfoInverseComparator =
+ new Comparator<WindowInfo>() {
+ @Override
+ public int compare(WindowInfo lhs, WindowInfo rhs) {
+ if (lhs.layer != rhs.layer) {
+ return rhs.layer - lhs.layer;
+ }
+ if (lhs.touchableRegion.top != rhs.touchableRegion.top) {
+ return rhs.touchableRegion.top - lhs.touchableRegion.top;
+ }
+ if (lhs.touchableRegion.left != rhs.touchableRegion.left) {
+ return rhs.touchableRegion.left - lhs.touchableRegion.left;
+ }
+ if (lhs.touchableRegion.right != rhs.touchableRegion.right) {
+ return rhs.touchableRegion.right - lhs.touchableRegion.right;
+ }
+ if (lhs.touchableRegion.bottom != rhs.touchableRegion.bottom) {
+ return rhs.touchableRegion.bottom - lhs.touchableRegion.bottom;
+ }
+ return 0;
+ }
+ };
+
+ public void recomputeBounds(boolean animate) {
+ Rect magnifiedFrame = mTempRect1;
+ magnifiedFrame.set(0, 0, 0, 0);
+
+ Rect notMagnifiedFrame = mTempRect2;
+ notMagnifiedFrame.set(0, 0, 0, 0);
+
+ ArrayList<WindowInfo> infos = mTempWindowInfoList;
+ infos.clear();
+ int windowCount = 0;
+ try {
+ mWindowManagerService.getVisibleWindowsForDisplay(
+ mDisplayProvider.getDisplay().getDisplayId(), infos);
+ Collections.sort(infos, mWindowInfoInverseComparator);
+ windowCount = infos.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo info = infos.get(i);
+ if (info.type == WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
+ continue;
+ }
+ if (isWindowMagnified(info.type)) {
+ Rect clippedFrame = mTempRect3;
+ clippedFrame.set(info.touchableRegion);
+ subtract(clippedFrame, notMagnifiedFrame);
+ magnifiedFrame.union(clippedFrame);
+ } else {
+ Rect clippedFrame = mTempRect3;
+ clippedFrame.set(info.touchableRegion);
+ subtract(clippedFrame, magnifiedFrame);
+ notMagnifiedFrame.union(clippedFrame);
+ }
+ if (magnifiedFrame.bottom >= notMagnifiedFrame.top) {
+ break;
+ }
+ }
+ } catch (RemoteException re) {
+ /* ignore */
+ } finally {
+ for (int i = windowCount - 1; i >= 0; i--) {
+ infos.remove(i).recycle();
+ }
+ }
+
+ final int displayWidth = mDisplayProvider.getDisplayInfo().logicalWidth;
+ final int displayHeight = mDisplayProvider.getDisplayInfo().logicalHeight;
+ magnifiedFrame.intersect(0, 0, displayWidth, displayHeight);
+
+ resize(magnifiedFrame, animate);
+ }
+
+ private boolean isWindowMagnified(int type) {
+ return (type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+ && type != WindowManager.LayoutParams.TYPE_INPUT_METHOD
+ && type != WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+ }
+
+ public void rotationChanged() {
+ mViewportFrame.rotationChanged();
+ }
+
+ public Rect getBounds() {
+ return mViewportFrame.getBounds();
+ }
+
+ public void setFrameShown(boolean shown, boolean animate) {
+ if (mViewportFrame.isShown() == shown) {
+ return;
+ }
+ if (animate) {
+ if (mShowHideFrameAnimator.isRunning()) {
+ mShowHideFrameAnimator.reverse();
+ } else {
+ if (shown) {
+ mViewportFrame.show();
+ mShowHideFrameAnimator.start();
+ } else {
+ mShowHideFrameAnimator.reverse();
+ }
+ }
+ } else {
+ mShowHideFrameAnimator.cancel();
+ if (shown) {
+ mViewportFrame.show();
+ } else {
+ mViewportFrame.hide();
+ }
+ }
+ }
+
+ private void resize(Rect bounds, boolean animate) {
+ if (mViewportFrame.getBounds().equals(bounds)) {
+ return;
+ }
+ if (animate) {
+ if (mResizeFrameAnimator.isRunning()) {
+ mResizeFrameAnimator.cancel();
+ }
+ mResizeFrameAnimator.setObjectValues(mViewportFrame.mBounds, bounds);
+ mResizeFrameAnimator.start();
+ } else {
+ mViewportFrame.setBounds(bounds);
+ }
+ }
+
+ private boolean subtract(Rect lhs, Rect rhs) {
+ if (lhs.right < rhs.left || lhs.left > rhs.right
+ || lhs.bottom < rhs.top || lhs.top > rhs.bottom) {
+ return false;
+ }
+ if (lhs.left < rhs.left) {
+ lhs.right = rhs.left;
+ }
+ if (lhs.top < rhs.top) {
+ lhs.bottom = rhs.top;
+ }
+ if (lhs.right > rhs.right) {
+ lhs.left = rhs.right;
+ }
+ if (lhs.bottom > rhs.bottom) {
+ lhs.top = rhs.bottom;
+ }
+ return true;
+ }
+
+ private static final class ViewportWindow {
+ private static final String WINDOW_TITLE = "Magnification Overlay";
+
+ private final WindowManager mWindowManager;
+ private final DisplayProvider mDisplayProvider;
+
+ private final ContentView mWindowContent;
+ private final WindowManager.LayoutParams mWindowParams;
+
+ private final Rect mBounds = new Rect();
+ private boolean mShown;
+ private int mAlpha;
+
+ public ViewportWindow(Context context, WindowManager windowManager,
+ DisplayProvider displayProvider) {
+ mWindowManager = windowManager;
+ mDisplayProvider = displayProvider;
+
+ ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ mWindowContent = new ContentView(context);
+ mWindowContent.setLayoutParams(contentParams);
+ mWindowContent.setBackgroundColor(R.color.transparent);
+
+ mWindowParams = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY);
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mWindowParams.setTitle(WINDOW_TITLE);
+ mWindowParams.gravity = Gravity.CENTER;
+ mWindowParams.width = displayProvider.getDisplayInfo().logicalWidth;
+ mWindowParams.height = displayProvider.getDisplayInfo().logicalHeight;
+ mWindowParams.format = PixelFormat.TRANSLUCENT;
+ }
+
+ public boolean isShown() {
+ return mShown;
+ }
+
+ public void show() {
+ if (mShown) {
+ return;
+ }
+ mShown = true;
+ mWindowManager.addView(mWindowContent, mWindowParams);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow shown.");
+ }
+ }
+
+ public void hide() {
+ if (!mShown) {
+ return;
+ }
+ mShown = false;
+ mWindowManager.removeView(mWindowContent);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow hidden.");
+ }
+ }
+
+ @SuppressWarnings("unused")
+ // Called reflectively from an animator.
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @SuppressWarnings("unused")
+ // Called reflectively from an animator.
+ public void setAlpha(int alpha) {
+ if (mAlpha == alpha) {
+ return;
+ }
+ mAlpha = alpha;
+ if (mShown) {
+ mWindowContent.invalidate();
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportFrame set alpha: " + alpha);
+ }
+ }
+
+ public Rect getBounds() {
+ return mBounds;
+ }
+
+ public void rotationChanged() {
+ mWindowParams.width = mDisplayProvider.getDisplayInfo().logicalWidth;
+ mWindowParams.height = mDisplayProvider.getDisplayInfo().logicalHeight;
+ if (mShown) {
+ mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
+ }
+ }
+
+ public void setBounds(Rect bounds) {
+ if (mBounds.equals(bounds)) {
+ return;
+ }
+ mBounds.set(bounds);
+ if (mShown) {
+ mWindowContent.invalidate();
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportFrame set bounds: " + bounds);
+ }
+ }
+
+ private final class ContentView extends View {
+ private final Drawable mHighlightFrame;
+
+ public ContentView(Context context) {
+ super(context);
+ mHighlightFrame = context.getResources().getDrawable(
+ R.drawable.magnified_region_frame);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+ mHighlightFrame.setBounds(mBounds);
+ mHighlightFrame.setAlpha(mAlpha);
+ mHighlightFrame.draw(canvas);
+ }
+ }
+ }
+ }
+
+ private static class DisplayProvider implements DisplayListener {
+ private final WindowManager mWindowManager;
+ private final DisplayManager mDisplayManager;
+ private final Display mDefaultDisplay;
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+
+ public DisplayProvider(Context context, WindowManager windowManager) {
+ mWindowManager = windowManager;
+ mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mDefaultDisplay = mWindowManager.getDefaultDisplay();
+ mDisplayManager.registerDisplayListener(this, null);
+ updateDisplayInfo();
+ }
+
+ public DisplayInfo getDisplayInfo() {
+ return mDefaultDisplayInfo;
+ }
+
+ public Display getDisplay() {
+ return mDefaultDisplay;
+ }
+
+ private void updateDisplayInfo() {
+ if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) {
+ Slog.e(LOG_TAG, "Default display is not valid.");
+ }
+ }
+
+ public void destroy() {
+ mDisplayManager.unregisterDisplayListener(this);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ /* do noting */
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ // Having no default display
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updateDisplayInfo();
+ }
+ }
+}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 48c6b2a..cb6b31a 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -25,6 +25,7 @@ import android.gesture.GestureStore;
import android.gesture.GestureStroke;
import android.gesture.Prediction;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
@@ -35,9 +36,9 @@ import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
-import com.android.server.input.InputFilter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -64,7 +65,7 @@ import java.util.Arrays;
*
* @hide
*/
-public class TouchExplorer {
+class TouchExplorer implements EventStreamTransformation {
private static final boolean DEBUG = false;
@@ -120,10 +121,6 @@ public class TouchExplorer {
// Slop between the first and second tap to be a double tap.
private final int mDoubleTapSlop;
- // The InputFilter this tracker is associated with i.e. the filter
- // which delegates event processing to this touch explorer.
- private final InputFilter mInputFilter;
-
// The current state of the touch explorer.
private int mCurrentState = STATE_TOUCH_EXPLORING;
@@ -155,6 +152,9 @@ public class TouchExplorer {
// The scaled velocity above which we detect gestures.
private final int mScaledGestureDetectionVelocity;
+ // The handler to which to delegate events.
+ private EventStreamTransformation mNext;
+
// Helper to track gesture velocity.
private VelocityTracker mVelocityTracker;
@@ -170,6 +170,9 @@ public class TouchExplorer {
// Temporary rectangle to avoid instantiation.
private final Rect mTempRect = new Rect();
+ // Context in which this explorer operates.
+ private final Context mContext;
+
// The X of the previous event.
private float mPreviousX;
@@ -200,20 +203,25 @@ public class TouchExplorer {
// The id of the last touch explored window.
private int mLastTouchedWindowId;
+ // Whether touch exploration gesture has ended.
+ private boolean mTouchExplorationGestureEnded;
+
+ // Whether touch interaction has ended.
+ private boolean mTouchInteractionEnded;
+
/**
* Creates a new instance.
*
* @param inputFilter The input filter associated with this explorer.
* @param context A context handle for accessing resources.
*/
- public TouchExplorer(InputFilter inputFilter, Context context,
- AccessibilityManagerService service) {
+ public TouchExplorer(Context context, AccessibilityManagerService service) {
+ mContext = context;
mAms = service;
mReceivedPointerTracker = new ReceivedPointerTracker(context);
mInjectedPointerTracker = new InjectedPointerTracker();
- mInputFilter = inputFilter;
mTapTimeout = ViewConfiguration.getTapTimeout();
- mDetermineUserIntentTimeout = (int) (mTapTimeout * 1.5f);
+ mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
@@ -242,7 +250,11 @@ public class TouchExplorer {
}
}
- public void clear(MotionEvent event, int policyFlags) {
+ public void onDestroy() {
+ // TODO: Implement
+ }
+
+ private void clear(MotionEvent event, int policyFlags) {
switch (mCurrentState) {
case STATE_TOUCH_EXPLORING: {
// If a touch exploration gesture is in progress send events for its end.
@@ -278,8 +290,17 @@ public class TouchExplorer {
mLongPressingPointerDeltaX = 0;
mLongPressingPointerDeltaY = 0;
mCurrentState = STATE_TOUCH_EXPLORING;
+ if (mNext != null) {
+ mNext.clear();
+ }
}
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent event, int policyFlags) {
if (DEBUG) {
Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
@@ -308,9 +329,26 @@ public class TouchExplorer {
}
public void onAccessibilityEvent(AccessibilityEvent event) {
+ final int eventType = event.getEventType();
+
+ // The event for gesture end should be strictly after the
+ // last hover exit event.
+ if (mTouchExplorationGestureEnded
+ && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
+ mTouchExplorationGestureEnded = false;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ }
+
+ // The event for touch interaction end should be strictly after the
+ // last hover exit and the touch exploration gesture end events.
+ if (mTouchInteractionEnded
+ && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
+ mTouchInteractionEnded = false;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ }
+
// If a new window opens or the accessibility focus moves we no longer
// want to click/long press on the last touch explored location.
- final int eventType = event.getEventType();
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
@@ -325,6 +363,9 @@ public class TouchExplorer {
mLastTouchedWindowId = event.getWindowId();
} break;
}
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
}
/**
@@ -346,6 +387,15 @@ public class TouchExplorer {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ // The delayed enter not delivered implies that we have delivered
+ // TYPE_TOUCH_INTERACTION_START and not TYPE_TOUCH_INTERACTION_END,
+ // therefore we need to deliver the interaction end event here.
+ if (mSendHoverEnterDelayed.isPending()) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ }
+ // Announce the start of a new touch interaction.
+ sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
// Pre-feed the motion events to the gesture detector since we
// have a distance slop before getting into gesture detection
// mode and not using the points within this slop significantly
@@ -384,7 +434,7 @@ public class TouchExplorer {
// to detect what the user is trying to do.
final int pointerId = receivedTracker.getPrimaryActivePointerId();
final int pointerIdBits = (1 << pointerId);
- mSendHoverEnterDelayed.post(event, pointerIdBits, policyFlags);
+ mSendHoverEnterDelayed.post(event, true, pointerIdBits, policyFlags);
} break;
default: {
/* do nothing - let the code for ACTION_MOVE decide what to do */
@@ -431,6 +481,10 @@ public class TouchExplorer {
mSendHoverExitDelayed.remove();
mPerformLongPressDelayed.remove();
mExitGestureDetectionModeDelayed.post();
+ // Send accessibility event to announce the start
+ // of gesture recognition.
+ sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
} else {
// We have just decided that the user is touch,
// exploring so start sending events.
@@ -539,7 +593,8 @@ public class TouchExplorer {
// If we have not delivered the enter schedule exit.
if (mSendHoverEnterDelayed.isPending()) {
- mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
+ mSendHoverEnterDelayed.mTouchExplorationInProgress = false;
+ mSendHoverExitDelayed.post(event, false, pointerIdBits, policyFlags);
} else {
// The user is touch exploring so we send events for end.
sendExitEventsIfNeeded(policyFlags);
@@ -644,6 +699,9 @@ public class TouchExplorer {
}
} break;
case MotionEvent.ACTION_UP: {
+ // Announce the end of a new touch interaction.
+ sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
mCurrentState = STATE_TOUCH_EXPLORING;
} break;
case MotionEvent.ACTION_CANCEL: {
@@ -675,6 +733,10 @@ public class TouchExplorer {
}
} break;
case MotionEvent.ACTION_UP:
+ // Announce the end of a new touch interaction.
+ sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ //$FALL-THROUGH$
case MotionEvent.ACTION_POINTER_UP: {
mLongPressingPointerId = -1;
mLongPressingPointerDeltaX = 0;
@@ -713,6 +775,13 @@ public class TouchExplorer {
}
} break;
case MotionEvent.ACTION_UP: {
+ // Announce the end of gesture recognition.
+ sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+ // Announce the end of a new touch interaction.
+ sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
float x = event.getX();
float y = event.getY();
mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
@@ -748,6 +817,19 @@ public class TouchExplorer {
}
/**
+ * Sends an accessibility event of the given type.
+ *
+ * @param type The event type.
+ */
+ private void sendAccessibilityEvent(int type) {
+ AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
+ if (accessibilityManager.isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ accessibilityManager.sendAccessibilityEvent(event);
+ }
+ }
+
+ /**
* Sends down events to the view hierarchy for all active pointers which are
* not already being delivered i.e. pointers that are not yet injected.
*
@@ -795,7 +877,8 @@ public class TouchExplorer {
MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
- mAms.touchExplorationGestureEnded();
+ mTouchExplorationGestureEnded = true;
+ mTouchInteractionEnded = true;
sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
}
}
@@ -810,7 +893,6 @@ public class TouchExplorer {
MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
- mAms.touchExplorationGestureStarted();
sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
}
}
@@ -958,7 +1040,9 @@ public class TouchExplorer {
// Make sure that the user will see the event.
policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- mInputFilter.sendInputEvent(event, policyFlags);
+ if (mNext != null) {
+ mNext.onMotionEvent(event, policyFlags);
+ }
mInjectedPointerTracker.onMotionEvent(event);
@@ -1008,11 +1092,13 @@ public class TouchExplorer {
private MotionEvent mFirstTapEvent;
public void onMotionEvent(MotionEvent event, int policyFlags) {
+ final int actionIndex = event.getActionIndex();
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
- if (mFirstTapEvent != null && !isSamePointerContext(mFirstTapEvent, event)) {
+ if (mFirstTapEvent != null
+ && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) {
clear();
}
mDownEvent = MotionEvent.obtain(event);
@@ -1022,19 +1108,21 @@ public class TouchExplorer {
if (mDownEvent == null) {
return;
}
- if (!isSamePointerContext(mDownEvent, event)) {
+ if (!GestureUtils.isSamePointerContext(mDownEvent, event)) {
clear();
return;
}
- if (isTap(mDownEvent, event)) {
- if (mFirstTapEvent == null || isTimedOut(mFirstTapEvent, event,
- mDoubleTapTimeout)) {
+ if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop,
+ actionIndex)) {
+ if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent,
+ event, mDoubleTapTimeout)) {
mFirstTapEvent = MotionEvent.obtain(event);
mDownEvent.recycle();
mDownEvent = null;
return;
}
- if (isDoubleTap(mFirstTapEvent, event)) {
+ if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout,
+ mDoubleTapSlop, actionIndex)) {
onDoubleTap(event, policyFlags);
mFirstTapEvent.recycle();
mFirstTapEvent = null;
@@ -1062,16 +1150,24 @@ public class TouchExplorer {
return;
}
+ if (Build.IS_DEBUGGABLE) {
+ if (mSendHoverEnterDelayed.isPending()) {
+ throw new IllegalStateException("mSendHoverEnterDelayed must not be pending.");
+ }
+ if (mSendHoverExitDelayed.isPending()) {
+ throw new IllegalStateException("mSendHoverExitDelayed must not be pending.");
+ }
+ if (!mPerformLongPressDelayed.isPending()) {
+ throw new IllegalStateException(
+ "mPerformLongPressDelayed must not be pending.");
+ }
+ }
+
// Remove pending event deliveries.
- mSendHoverEnterDelayed.remove();
- mSendHoverExitDelayed.remove();
mPerformLongPressDelayed.remove();
- // This is a tap so do not send hover events since
- // this events will result in firing the corresponding
- // accessibility events confusing the user about what
- // is actually clicked.
- sendExitEventsIfNeeded(policyFlags);
+ // The touch interaction has ended since we will send a click.
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
int clickLocationX;
int clickLocationY;
@@ -1140,42 +1236,6 @@ public class TouchExplorer {
}
}
- public boolean isTap(MotionEvent down, MotionEvent up) {
- return eventsWithinTimeoutAndDistance(down, up, mTapTimeout, mTouchSlop);
- }
-
- private boolean isDoubleTap(MotionEvent firstUp, MotionEvent secondUp) {
- return eventsWithinTimeoutAndDistance(firstUp, secondUp, mDoubleTapTimeout,
- mDoubleTapSlop);
- }
-
- private boolean eventsWithinTimeoutAndDistance(MotionEvent first, MotionEvent second,
- int timeout, int distance) {
- if (isTimedOut(first, second, timeout)) {
- return false;
- }
- final int downPtrIndex = first.getActionIndex();
- final int upPtrIndex = second.getActionIndex();
- final float deltaX = second.getX(upPtrIndex) - first.getX(downPtrIndex);
- final float deltaY = second.getY(upPtrIndex) - first.getY(downPtrIndex);
- final double deltaMove = Math.hypot(deltaX, deltaY);
- if (deltaMove >= distance) {
- return false;
- }
- return true;
- }
-
- private boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
- final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
- return (deltaTime >= timeout);
- }
-
- private boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
- return (first.getPointerIdBits() == second.getPointerIdBits()
- && first.getPointerId(first.getActionIndex())
- == second.getPointerId(second.getActionIndex()));
- }
-
public boolean firstTapDetected() {
return mFirstTapEvent != null
&& SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout;
@@ -1201,47 +1261,14 @@ public class TouchExplorer {
final float secondPtrX = event.getX(secondPtrIndex);
final float secondPtrY = event.getY(secondPtrIndex);
- // Check if the pointers are moving in the same direction.
- final float firstDeltaX =
- firstPtrX - receivedTracker.getReceivedPointerDownX(firstPtrIndex);
- final float firstDeltaY =
- firstPtrY - receivedTracker.getReceivedPointerDownY(firstPtrIndex);
+ final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(firstPtrIndex);
+ final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(firstPtrIndex);
+ final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(secondPtrIndex);
+ final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(secondPtrIndex);
- if (firstDeltaX == 0 && firstDeltaY == 0) {
- return true;
- }
-
- final float firstMagnitude =
- (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY);
- final float firstXNormalized =
- (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
- final float firstYNormalized =
- (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
-
- final float secondDeltaX =
- secondPtrX - receivedTracker.getReceivedPointerDownX(secondPtrIndex);
- final float secondDeltaY =
- secondPtrY - receivedTracker.getReceivedPointerDownY(secondPtrIndex);
-
- if (secondDeltaX == 0 && secondDeltaY == 0) {
- return true;
- }
-
- final float secondMagnitude =
- (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY);
- final float secondXNormalized =
- (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
- final float secondYNormalized =
- (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
-
- final float angleCos =
- firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
-
- if (angleCos < MAX_DRAGGING_ANGLE_COS) {
- return false;
- }
-
- return true;
+ return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX,
+ secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY,
+ MAX_DRAGGING_ANGLE_COS);
}
/**
@@ -1308,13 +1335,13 @@ public class TouchExplorer {
}
public void remove() {
- if (isPenidng()) {
+ if (isPending()) {
mHandler.removeCallbacks(this);
clear();
}
}
- private boolean isPenidng() {
+ public boolean isPending() {
return (mEvent != null);
}
@@ -1377,7 +1404,7 @@ public class TouchExplorer {
}
private void clear() {
- if (!isPenidng()) {
+ if (!isPending()) {
return;
}
mEvent.recycle();
@@ -1398,15 +1425,18 @@ public class TouchExplorer {
private MotionEvent mPrototype;
private int mPointerIdBits;
private int mPolicyFlags;
+ private boolean mTouchExplorationInProgress;
public SendHoverDelayed(int hoverAction, boolean gestureStarted) {
mHoverAction = hoverAction;
mGestureStarted = gestureStarted;
}
- public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
+ public void post(MotionEvent prototype, boolean touchExplorationInProgress,
+ int pointerIdBits, int policyFlags) {
remove();
mPrototype = MotionEvent.obtain(prototype);
+ mTouchExplorationInProgress = touchExplorationInProgress;
mPointerIdBits = pointerIdBits;
mPolicyFlags = policyFlags;
mHandler.postDelayed(this, mDetermineUserIntentTimeout);
@@ -1443,6 +1473,7 @@ public class TouchExplorer {
mPrototype = null;
mPointerIdBits = -1;
mPolicyFlags = 0;
+ mTouchExplorationInProgress = false;
}
public void forceSendAndRemove() {
@@ -1459,10 +1490,17 @@ public class TouchExplorer {
Slog.d(LOG_TAG_SEND_HOVER_DELAYED, mGestureStarted ?
"touchExplorationGestureStarted" : "touchExplorationGestureEnded");
}
- if (mGestureStarted) {
- mAms.touchExplorationGestureStarted();
+ if (mTouchExplorationInProgress) {
+ if (mGestureStarted) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
+ } else {
+ mTouchExplorationGestureEnded = true;
+ mTouchInteractionEnded = true;
+ }
} else {
- mAms.touchExplorationGestureEnded();
+ if (!mGestureStarted) {
+ mTouchInteractionEnded = true;
+ }
}
sendMotionEvent(mPrototype, mHoverAction, mPointerIdBits, mPolicyFlags);
clear();
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..aefc264
--- /dev/null
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -0,0 +1,2158 @@
+/*
+ * 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<ComponentName, ServiceRecord> 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<Intent.FilterComparison, ServiceRecord> 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, UserHandle.getCallingUserId());
+ 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 int userId = UserHandle.getCallingUserId();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ ServiceRecord r = findServiceLocked(className, token, userId);
+ 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));
+ 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;
+ }
+ 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, int userId) {
+ ServiceRecord r = mServiceMap.getServiceByName(name, userId);
+ 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);
+
+ userId = mAm.handleIncomingUserLocked(callingPid, callingUid, userId,
+ false, true, "service", 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,
+ 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);
+
+ // Make sure that the user who owns this service is started. If not,
+ // we don't want to allow it to run.
+ if (mAm.mStartedUsers.get(r.userId) == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + r.appInfo.packageName + "/"
+ + r.appInfo.uid + " for service "
+ + r.intent.getIntent() + ": user " + r.userId + " is stopped");
+ bringDownServiceLocked(r, true);
+ return false;
+ }
+
+ // 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;
+ }
+ }
+ 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);
+ }
+ }
+ }
+
+ private boolean collectForceStopServicesLocked(String name, int userId,
+ boolean evenPersistent, boolean doit,
+ HashMap<ComponentName, ServiceRecord> services,
+ ArrayList<ServiceRecord> result) {
+ boolean didSomething = false;
+ for (ServiceRecord service : services.values()) {
+ if ((name == null || 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;
+ result.add(service);
+ }
+ }
+ return didSomething;
+ }
+
+ boolean forceStopLocked(String name, int userId, boolean evenPersistent, boolean doit) {
+ boolean didSomething = false;
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ if (userId == UserHandle.USER_ALL) {
+ for (int i=0; i<mServiceMap.mServicesByNamePerUser.size(); i++) {
+ didSomething |= collectForceStopServicesLocked(name, userId, evenPersistent,
+ doit, mServiceMap.mServicesByNamePerUser.valueAt(i), services);
+ if (!doit && didSomething) {
+ return true;
+ }
+ }
+ } else {
+ HashMap<ComponentName, ServiceRecord> items
+ = mServiceMap.mServicesByNamePerUser.get(userId);
+ if (items != null) {
+ didSomething = collectForceStopServicesLocked(name, userId, evenPersistent,
+ doit, items, services);
+ }
+ }
+
+ 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) {
+ int[] users = mAm.getUsersLocked();
+ for (int ui=0; ui<users.length && res.size() < maxNum; ui++) {
+ if (mServiceMap.getAllServices(users[ui]).size() > 0) {
+ Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+ users[ui]).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 {
+ int[] users = mAm.getUsersLocked();
+ for (int user : users) {
+ if (mServiceMap.getAllServices(user).size() > 0) {
+ boolean printed = false;
+ long nowReal = SystemClock.elapsedRealtime();
+ Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+ user).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 (user != 0) {
+ pw.println();
+ }
+ pw.println(" User " + user + " 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>();
+
+ synchronized (this) {
+ int[] users = mAm.getUsersLocked();
+ if ("all".equals(name)) {
+ for (int user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user)) {
+ 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) {
+ }
+ }
+
+ for (int user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user)) {
+ 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 5d74cf3..ce5424b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -27,6 +27,7 @@ import com.android.server.ProcessMap;
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
import dalvik.system.Zygote;
@@ -46,12 +47,13 @@ import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
+import android.app.IStopUserCallback;
import android.app.IThumbnailReceiver;
+import android.app.IUserSwitchObserver;
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 +78,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;
@@ -99,6 +101,8 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.IPermissionController;
+import android.os.IRemoteCallback;
+import android.os.IUserManager;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -111,7 +115,7 @@ 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.provider.Settings;
import android.text.format.Time;
import android.util.EventLog;
@@ -120,7 +124,6 @@ import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -144,7 +147,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;
@@ -153,7 +155,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -242,37 +243,16 @@ 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;
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
+ // Amount of time we wait for observers to handle a user switch before
+ // giving up on them and unfreezing the screen.
+ static final int USER_SWITCH_TIMEOUT = 2*1000;
+
static final int MY_PID = Process.myPid();
static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -454,6 +434,27 @@ public final class ActivityManagerService extends ActivityManagerNative
long mPreviousProcessVisibleTime;
/**
+ * Which uses have been started, so are allowed to run code.
+ */
+ final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
+
+ /**
+ * LRU list of history of current users. Most recently current is at the end.
+ */
+ final ArrayList<Integer> mUserLru = new ArrayList<Integer>();
+
+ /**
+ * Registered observers of the user switching mechanics.
+ */
+ final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
+ = new RemoteCallbackList<IUserSwitchObserver>();
+
+ /**
+ * Currently active user switch.
+ */
+ Object mCurUserSwitchCallback;
+
+ /**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
@@ -514,48 +515,36 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
+ if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
+ || userId == filter.owningUserId) {
+ return super.newResult(filter, match, userId);
+ }
+ return null;
+ }
+
+ @Override
+ protected BroadcastFilter[] newArray(int size) {
+ return new BroadcastFilter[size];
+ }
+
+ @Override
protected String packageForFilter(BroadcastFilter filter) {
return filter.packageName;
}
};
/**
- * State of all active sticky broadcasts. Keys are the action of the
+ * State of all active sticky broadcasts per user. Keys are the action of the
* sticky Intent, values are an ArrayList of all broadcasted intents with
- * that action (which should usually be one).
- */
- 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.
+ * that action (which should usually be one). The SparseArray is keyed
+ * by the user ID the sticky is for, and can include UserHandle.USER_ALL
+ * for stickies that are sent to all users.
*/
- final ArrayList<ServiceRecord> mRestartingServices
- = new ArrayList<ServiceRecord>();
+ final SparseArray<HashMap<String, ArrayList<Intent>>> mStickyBroadcasts =
+ new SparseArray<HashMap<String, ArrayList<Intent>>>();
- /**
- * 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
@@ -575,7 +564,7 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
final ArrayList mCancelledThumbnails = new ArrayList();
- final ProviderMap mProviderMap = new ProviderMap();
+ final ProviderMap mProviderMap;
/**
* List of content providers who have clients waiting for them. The
@@ -733,6 +722,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 +828,9 @@ public final class ActivityManagerService extends ActivityManagerNative
static ActivityManagerService mSelf;
static ActivityThread mSystemThread;
+ private int mCurrentUserId;
+ private UserManagerService mUserManager;
+
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
@@ -876,6 +880,9 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int DISPATCH_PROCESSES_CHANGED = 31;
static final int DISPATCH_PROCESS_DIED = 32;
static final int REPORT_MEM_USAGE = 33;
+ static final int REPORT_USER_SWITCH_MSG = 34;
+ static final int CONTINUE_USER_SWITCH_MSG = 35;
+ static final int USER_SWITCH_TIMEOUT_MSG = 36;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1010,10 +1017,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) {
@@ -1111,11 +1118,11 @@ public final class ActivityManagerService extends ActivityManagerNative
} break;
case KILL_APPLICATION_MSG: {
synchronized (ActivityManagerService.this) {
- int uid = msg.arg1;
+ int appid = msg.arg1;
boolean restart = (msg.arg2 == 1);
String pkg = (String) msg.obj;
- forceStopPackageLocked(pkg, uid, restart, false, true, false,
- UserId.getUserId(uid));
+ forceStopPackageLocked(pkg, appid, restart, false, true, false,
+ UserHandle.USER_ALL);
}
} break;
case FINALIZE_PENDING_INTENT_MSG: {
@@ -1147,13 +1154,15 @@ public final class ActivityManagerService extends ActivityManagerNative
notification.vibrate = null;
notification.setLatestEventInfo(context, text,
mContext.getText(R.string.heavy_weight_notification_detail),
- PendingIntent.getActivity(mContext, 0, root.intent,
- PendingIntent.FLAG_CANCEL_CURRENT));
+ PendingIntent.getActivityAsUser(mContext, 0, root.intent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(root.userId)));
try {
int[] outId = new int[1];
- inm.enqueueNotification("android", R.string.heavy_weight_notification,
- notification, outId);
+ inm.enqueueNotificationWithTag("android", null,
+ R.string.heavy_weight_notification,
+ notification, outId, root.userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for heavy-weight app", e);
@@ -1169,8 +1178,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
try {
- inm.cancelNotification("android",
- R.string.heavy_weight_notification);
+ inm.cancelNotificationWithTag("android", null,
+ R.string.heavy_weight_notification, msg.arg1);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
@@ -1284,7 +1293,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);
}
@@ -1303,6 +1313,18 @@ public final class ActivityManagerService extends ActivityManagerNative
thread.start();
break;
}
+ case REPORT_USER_SWITCH_MSG: {
+ dispatchUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+ break;
+ }
+ case CONTINUE_USER_SWITCH_MSG: {
+ continueUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+ break;
+ }
+ case USER_SWITCH_TIMEOUT_MSG: {
+ timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+ break;
+ }
}
}
};
@@ -1521,6 +1543,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
+ mServices = new ActiveServices(this);
+ mProviderMap = new ProviderMap(this);
+
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
@@ -1536,11 +1561,16 @@ public final class ActivityManagerService extends ActivityManagerNative
systemDir, "usagestats").toString());
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
+ // User 0 is the first and only user that runs at boot.
+ mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true));
+ mUserLru.add(Integer.valueOf(0));
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
mConfiguration.setToDefaults();
- mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLocale(Locale.getDefault());
+
mConfigurationSeq = mConfiguration.seq = 1;
mProcessStats.init();
@@ -1764,7 +1794,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private final void updateLruProcessInternalLocked(ProcessRecord app,
- boolean oomAdj, boolean updateActivityTime, int bestPos) {
+ boolean updateActivityTime, int bestPos) {
// put it on the LRU to keep track of when it should be exited.
int lrui = mLruProcesses.indexOf(app);
if (lrui >= 0) mLruProcesses.remove(lrui);
@@ -1796,7 +1826,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Also don't let it kick out the first few "real" hidden processes.
skipTop = ProcessList.MIN_HIDDEN_APPS;
}
-
+
while (i >= 0) {
ProcessRecord p = mLruProcesses.get(i);
// If this app shouldn't be in front of the first N background
@@ -1821,7 +1851,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (cr.binding != null && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq) {
- updateLruProcessInternalLocked(cr.binding.service.app, false,
+ updateLruProcessInternalLocked(cr.binding.service.app,
updateActivityTime, i+1);
}
}
@@ -1829,21 +1859,21 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
- updateLruProcessInternalLocked(cpr.proc, false,
+ updateLruProcessInternalLocked(cpr.proc,
updateActivityTime, i+1);
}
}
-
- //Slog.i(TAG, "Putting proc to front: " + app.processName);
- if (oomAdj) {
- updateOomAdjLocked();
- }
}
final void updateLruProcessLocked(ProcessRecord app,
boolean oomAdj, boolean updateActivityTime) {
mLruSeq++;
- updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
+ updateLruProcessInternalLocked(app, updateActivityTime, 0);
+
+ //Slog.i(TAG, "Putting proc to front: " + app.processName);
+ if (oomAdj) {
+ updateOomAdjLocked();
+ }
}
final ProcessRecord getProcessRecordLocked(
@@ -1857,7 +1887,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 +2022,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 +2038,37 @@ public final class ActivityManagerService extends ActivityManagerNative
int uid = app.uid;
int[] gids = null;
+ int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
+ int[] permGids = null;
try {
- gids = mContext.getPackageManager().getPackageGids(
- app.info.packageName);
+ final PackageManager pm = mContext.getPackageManager();
+ permGids = pm.getPackageGids(app.info.packageName);
+
+ if (Environment.isExternalStorageEmulated()) {
+ if (pm.checkPermission(
+ android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
+ app.info.packageName) == PERMISSION_GRANTED) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+ } else {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
+ }
+ }
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
+
+ /*
+ * Add shared application GID so applications can share some
+ * resources like shared libraries
+ */
+ if (permGids == null) {
+ gids = new int[1];
+ } else {
+ gids = new int[permGids.length + 1];
+ System.arraycopy(permGids, 0, gids, 1, permGids.length);
+ }
+ gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
}
if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
@@ -2053,7 +2107,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 +2149,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 +2161,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);
}
}
@@ -2143,8 +2197,7 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.addCategory(Intent.CATEGORY_HOME);
}
ActivityInfo aInfo =
- intent.resolveActivityInfo(mContext.getPackageManager(),
- STOCK_PM_FLAGS);
+ resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
@@ -2164,6 +2217,29 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
+ private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {
+ ActivityInfo ai = null;
+ ComponentName comp = intent.getComponent();
+ try {
+ if (comp != null) {
+ ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+ } else {
+ ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+
+ if (info != null) {
+ ai = info.activityInfo;
+ }
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+
+ return ai;
+ }
+
/**
* Starts the "new version setup screen" if appropriate.
*/
@@ -2223,7 +2299,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,21 +2427,17 @@ 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;
- } 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;
- } else {
- userId = Binder.getOrigCallingUser();
- }
- }
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivity", null);
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
null, null, options, userId);
@@ -2374,24 +2446,27 @@ public final class ActivityManagerService extends ActivityManagerNative
public final WaitResult startActivityAndWait(IApplicationThread caller,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, String profileFile,
- ParcelFileDescriptor profileFd, Bundle options) {
+ ParcelFileDescriptor profileFd, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivityAndWait");
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityAndWait", null);
WaitResult res = new WaitResult();
- int userId = Binder.getOrigCallingUser();
mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
- res, null, options, userId);
+ res, null, options, UserHandle.getCallingUserId());
return res;
}
public final int startActivityWithConfig(IApplicationThread caller,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Configuration config,
- Bundle options) {
+ Bundle options, int userId) {
enforceNotIsolatedCaller("startActivityWithConfig");
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityWithConfig", null);
int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
- null, null, null, config, options, Binder.getOrigCallingUser());
+ null, null, null, config, options, userId);
return ret;
}
@@ -2456,7 +2531,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;
@@ -2522,18 +2597,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public final int startActivityInPackage(int uid,
+ final int startActivityInPackage(int uid,
Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options) {
-
- // 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(
- "startActivityInPackage only available to the system");
- }
+ String resultWho, int requestCode, int startFlags, Bundle options, int userId) {
+
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityInPackage", null);
int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
@@ -2545,23 +2614,18 @@ public final class ActivityManagerService extends ActivityManagerNative
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options) {
enforceNotIsolatedCaller("startActivities");
int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
- options, Binder.getOrigCallingUser());
+ options, UserHandle.getCallingUserId());
return ret;
}
- public final int startActivitiesInPackage(int uid,
+ final int startActivitiesInPackage(int uid,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options) {
+ Bundle options, int userId) {
- // This is so super not safe, that only the system (or okay root)
- // can do it.
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- throw new SecurityException(
- "startActivityInPackage only available to the system");
- }
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityInPackage", null);
int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
- options, UserId.getUserId(uid));
+ options, userId);
return ret;
}
@@ -2660,7 +2724,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final long origId = Binder.clearCallingIdentity();
boolean res = mMainStack.requestFinishActivityLocked(token, resultCode,
- resultData, "app-request");
+ resultData, "app-request", true);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -2690,13 +2754,14 @@ public final class ActivityManagerService extends ActivityManagerNative
int index = mMainStack.indexOfTokenLocked(r.appToken);
if (index >= 0) {
mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
- null, "finish-heavy");
+ null, "finish-heavy", true);
}
}
}
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
}
@@ -3379,10 +3444,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer, final int userId) {
+ final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
+ userId = handleIncomingUserLocked(pid, uid,
+ userId, false, true, "clearApplicationUserData", null);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -3424,7 +3491,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
- public void killBackgroundProcesses(final String packageName) {
+ public void killBackgroundProcesses(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED &&
checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES)
@@ -3436,22 +3503,23 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
- int userId = UserId.getCallingUserId();
+
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, "killBackgroundProcesses", null);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
- int pkgUid = -1;
synchronized(this) {
+ int appId = -1;
try {
- pkgUid = pm.getPackageUid(packageName, userId);
+ appId = UserHandle.getAppId(pm.getPackageUid(packageName, 0));
} catch (RemoteException e) {
}
- if (pkgUid == -1) {
+ if (appId == -1) {
Slog.w(TAG, "Invalid packageName: " + packageName);
return;
}
- killPackageProcessesLocked(packageName, pkgUid,
+ killPackageProcessesLocked(packageName, appId, userId,
ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
}
} finally {
@@ -3501,7 +3569,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public void forceStopPackage(final String packageName) {
+ public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: forceStopPackage() from pid="
@@ -3511,27 +3579,34 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- final int userId = UserId.getCallingUserId();
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, "forceStopPackage", null);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
- int pkgUid = -1;
synchronized(this) {
- try {
- pkgUid = pm.getPackageUid(packageName, userId);
- } catch (RemoteException e) {
- }
- if (pkgUid == -1) {
- Slog.w(TAG, "Invalid packageName: " + packageName);
- return;
- }
- forceStopPackageLocked(packageName, pkgUid);
- try {
- pm.setPackageStoppedState(packageName, true, userId);
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + packageName + ": " + e);
+ int[] users = userId == UserHandle.USER_ALL
+ ? getUsersLocked() : new int[] { userId };
+ for (int user : users) {
+ int pkgUid = -1;
+ try {
+ pkgUid = pm.getPackageUid(packageName, user);
+ } catch (RemoteException e) {
+ }
+ if (pkgUid == -1) {
+ Slog.w(TAG, "Invalid packageName: " + packageName);
+ continue;
+ }
+ try {
+ pm.setPackageStoppedState(packageName, true, user);
+ } catch (RemoteException e) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + packageName + ": " + e);
+ }
+ if (isUserRunningLocked(user)) {
+ forceStopPackageLocked(packageName, pkgUid);
+ }
}
}
} finally {
@@ -3540,16 +3615,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
/*
- * The pkg name and uid have to be specified.
- * @see android.app.IActivityManager#killApplicationWithUid(java.lang.String, int)
+ * The pkg name and app id have to be specified.
*/
- public void killApplicationWithUid(String pkg, int uid) {
+ public void killApplicationWithAppId(String pkg, int appid) {
if (pkg == null) {
return;
}
// Make sure the uid is valid.
- if (uid < 0) {
- Slog.w(TAG, "Invalid uid specified for pkg : " + pkg);
+ if (appid < 0) {
+ Slog.w(TAG, "Invalid appid specified for pkg : " + pkg);
return;
}
int callerUid = Binder.getCallingUid();
@@ -3557,7 +3631,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (callerUid == Process.SYSTEM_UID) {
// Post an aysnc message to kill the application
Message msg = mHandler.obtainMessage(KILL_APPLICATION_MSG);
- msg.arg1 = uid;
+ msg.arg1 = appid;
msg.arg2 = 0;
msg.obj = pkg;
mHandler.sendMessage(msg);
@@ -3570,33 +3644,50 @@ public final class ActivityManagerService extends ActivityManagerNative
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
+ final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
- synchronized (this) {
- closeSystemDialogsLocked(uid, reason);
+ try {
+ synchronized (this) {
+ // Only allow this from foreground processes, so that background
+ // applications can't abuse it to prevent system UI from being shown.
+ if (uid >= Process.FIRST_APPLICATION_UID) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ + " from background process " + proc);
+ return;
+ }
+ }
+ closeSystemDialogsLocked(reason);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
- void closeSystemDialogsLocked(int callingUid, String reason) {
+ void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
if (reason != null) {
intent.putExtra("reason", reason);
}
mWindowManager.closeSystemDialogs(reason);
-
+
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
r.stack.finishActivityLocked(r, i,
- Activity.RESULT_CANCELED, null, "close-sys");
+ Activity.RESULT_CANCELED, null, "close-sys", true);
}
}
-
+
broadcastIntentLocked(null, null, intent, null,
null, 0, null, null, null, false, false, -1,
- callingUid, 0 /* TODO: Verify */);
+ Process.SYSTEM_UID, UserHandle.USER_ALL);
}
public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
@@ -3647,7 +3738,8 @@ 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, UserHandle.getAppId(uid), false,
+ false, true, false, UserHandle.getUserId(uid));
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
@@ -3657,18 +3749,29 @@ 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,
- int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit,
- boolean evenPersistent, String reason) {
+
+ private void forceStopUserLocked(int userId) {
+ forceStopPackageLocked(null, -1, false, false, true, false, userId);
+ Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false,
+ MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+ }
+
+ private final boolean killPackageProcessesLocked(String packageName, int appId,
+ int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
+ boolean doit, boolean evenPersistent, String reason) {
ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
// Remove all processes this package may have touched: all with the
// same UID (except for the system or root user), and all whose name
// matches the package name.
- final String procNamePrefix = packageName + ":";
+ final String procNamePrefix = packageName != null ? (packageName + ":") : null;
for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
@@ -3681,20 +3784,41 @@ public final class ActivityManagerService extends ActivityManagerNative
if (doit) {
procs.add(app);
}
- // If uid is specified and the uid and process name match
- // Or, the uid is not specified and the process name matches
- } else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
- || ((app.processName.equals(packageName)
- || app.processName.startsWith(procNamePrefix))
- && uid < 0))) {
- if (app.setAdj >= minOomAdj) {
- if (!doit) {
- return true;
- }
- app.removed = true;
- procs.add(app);
+ continue;
+ }
+
+ // Skip process if it doesn't meet our oom adj requirement.
+ if (app.setAdj < minOomAdj) {
+ continue;
+ }
+
+ // If no package is specified, we call all processes under the
+ // give user id.
+ if (packageName == null) {
+ if (app.userId != userId) {
+ continue;
+ }
+ // Package has been specified, we want to hit all processes
+ // that match it. We need to qualify this by the processes
+ // that are running under the specified app and user ID.
+ } else {
+ if (UserHandle.getAppId(app.uid) != appId) {
+ continue;
+ }
+ if (userId != UserHandle.USER_ALL && app.userId != userId) {
+ continue;
+ }
+ if (!app.pkgList.contains(packageName)) {
+ continue;
}
}
+
+ // Process has passed all conditions, kill it!
+ if (!doit) {
+ return true;
+ }
+ app.removed = true;
+ procs.add(app);
}
}
@@ -3705,39 +3829,71 @@ public final class ActivityManagerService extends ActivityManagerNative
return N > 0;
}
- private final boolean forceStopPackageLocked(String name, int uid,
+ private final boolean forceStopPackageLocked(String name, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, int userId) {
int i;
int N;
- if (uid < 0) {
+ if (userId == UserHandle.USER_ALL && name == null) {
+ Slog.w(TAG, "Can't force stop all processes of all users, that is insane!");
+ }
+
+ if (appId < 0 && name != null) {
try {
- uid = AppGlobals.getPackageManager().getPackageUid(name, userId);
+ appId = UserHandle.getAppId(
+ AppGlobals.getPackageManager().getPackageUid(name, 0));
} catch (RemoteException e) {
}
}
if (doit) {
- Slog.i(TAG, "Force stopping package " + name + " uid=" + uid);
+ if (name != null) {
+ Slog.i(TAG, "Force stopping package " + name + " appid=" + appId
+ + " user=" + userId);
+ } else {
+ Slog.i(TAG, "Force stopping user " + userId);
+ }
Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
while (badApps.hasNext()) {
SparseArray<Long> ba = badApps.next();
- if (ba.get(uid) != null) {
+ for (i=ba.size()-1; i>=0; i--) {
+ boolean remove = false;
+ final int entUid = ba.keyAt(i);
+ if (name != null) {
+ if (userId == UserHandle.USER_ALL) {
+ if (UserHandle.getAppId(entUid) == appId) {
+ remove = true;
+ }
+ } else {
+ if (entUid == UserHandle.getUid(userId, appId)) {
+ remove = true;
+ }
+ }
+ } else if (UserHandle.getUserId(entUid) == userId) {
+ remove = true;
+ }
+ if (remove) {
+ ba.removeAt(i);
+ }
+ }
+ if (ba.size() == 0) {
badApps.remove();
}
}
}
-
- boolean didSomething = killPackageProcessesLocked(name, uid, -100,
- callerWillRestart, false, doit, evenPersistent, "force stop");
+
+ boolean didSomething = killPackageProcessesLocked(name, appId, userId,
+ -100, callerWillRestart, false, doit, evenPersistent,
+ name == null ? ("force stop user " + userId) : ("force stop " + name));
TaskRecord lastTask = null;
for (i=0; i<mMainStack.mHistory.size(); i++) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- final boolean samePackage = r.packageName.equals(name);
- if (r.userId == userId
+ final boolean samePackage = r.packageName.equals(name)
+ || (name == null && r.userId == userId);
+ if ((userId == UserHandle.USER_ALL || r.userId == userId)
&& (samePackage || r.task == lastTask)
&& (r.app == null || evenPersistent || !r.app.persistent)) {
if (!doit) {
@@ -3764,48 +3920,79 @@ 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;
}
+ didSomething = true;
}
- N = services.size();
- for (i=0; i<N; i++) {
- bringDownServiceLocked(services.get(i), true);
+ if (name == null) {
+ // Remove all sticky broadcasts from this user.
+ mStickyBroadcasts.remove(userId);
}
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
- for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) {
- if (provider.info.packageName.equals(name)
- && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
- if (!doit) {
- return true;
- }
- didSomething = true;
- providers.add(provider);
+ if (mProviderMap.collectForceStopProviders(name, appId, doit, evenPersistent,
+ userId, providers)) {
+ if (!doit) {
+ return true;
}
+ didSomething = true;
}
-
N = providers.size();
for (i=0; i<N; i++) {
removeDyingProviderLocked(null, providers.get(i), true);
}
+ if (mIntentSenderRecords.size() > 0) {
+ Iterator<WeakReference<PendingIntentRecord>> it
+ = mIntentSenderRecords.values().iterator();
+ while (it.hasNext()) {
+ WeakReference<PendingIntentRecord> wpir = it.next();
+ if (wpir == null) {
+ it.remove();
+ continue;
+ }
+ PendingIntentRecord pir = wpir.get();
+ if (pir == null) {
+ it.remove();
+ continue;
+ }
+ if (name == null) {
+ // Stopping user, remove all objects for the user.
+ if (pir.key.userId != userId) {
+ // Not the same user, skip it.
+ continue;
+ }
+ } else {
+ if (UserHandle.getAppId(pir.uid) != appId) {
+ // Different app id, skip it.
+ continue;
+ }
+ if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
+ // Different user, skip it.
+ continue;
+ }
+ if (!pir.key.packageName.equals(name)) {
+ // Different package, skip it.
+ continue;
+ }
+ }
+ if (!doit) {
+ return true;
+ }
+ didSomething = true;
+ it.remove();
+ pir.canceled = true;
+ if (pir.key.activity != null) {
+ pir.key.activity.pendingResults.remove(pir.ref);
+ }
+ }
+ }
+
if (doit) {
- if (purgeCache) {
+ if (purgeCache && name != null) {
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
ac.removePackage(name);
@@ -3831,8 +4018,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mProcessNames.remove(name, uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
@@ -3878,24 +4066,14 @@ public final class ActivityManagerService extends ActivityManagerNative
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
// 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 +4273,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;
}
}
@@ -4235,15 +4399,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}, pkgFilter);
- IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onUserRemoved(intent);
- }
- }, userFilter);
-
synchronized (this) {
// Ensure that any processes we had put on hold are now started
// up.
@@ -4265,12 +4420,19 @@ 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());
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ if (uss.mState == UserStartedState.STATE_BOOTING) {
+ uss.mState = UserStartedState.STATE_RUNNING;
+ final int userId = mStartedUsers.keyAt(i);
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
+ }
+ }
}
}
}
@@ -4293,7 +4455,13 @@ public final class ActivityManagerService extends ActivityManagerNative
enableScreenAfterBoot();
}
}
-
+
+ public final void activityResumed(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ mMainStack.activityResumed(token);
+ Binder.restoreCallingIdentity(origId);
+ }
+
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
mMainStack.activityPaused(token, false);
@@ -4380,7 +4548,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
- int flags, Bundle options) {
+ int flags, Bundle options, int userId) {
enforceNotIsolatedCaller("getIntentSender");
// Refuse possible leaked file descriptors
if (intents != null) {
@@ -4414,11 +4582,13 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
int callingUid = Binder.getCallingUid();
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), callingUid, userId,
+ false, true, "getIntentSender", null);
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()
@@ -4428,11 +4598,8 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
}
-
- if (DEBUG_MU)
- Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
- + Binder.getOrigCallingUid());
- return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
+
+ return getIntentSenderLocked(type, packageName, callingUid, userId,
token, resultWho, requestCode, intents, resolvedTypes, flags, options);
} catch (RemoteException e) {
@@ -4441,8 +4608,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- IIntentSender getIntentSenderLocked(int type,
- String packageName, int callingUid, IBinder token, String resultWho,
+ IIntentSender getIntentSenderLocked(int type, String packageName,
+ int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle options) {
if (DEBUG_MU)
@@ -4466,7 +4633,7 @@ public final class ActivityManagerService extends ActivityManagerNative
PendingIntentRecord.Key key = new PendingIntentRecord.Key(
type, packageName, activity, resultWho,
- requestCode, intents, resolvedTypes, flags, options);
+ requestCode, intents, resolvedTypes, flags, options, userId);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
@@ -4514,8 +4681,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 +4901,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 +4911,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 +5042,6 @@ public final class ActivityManagerService extends ActivityManagerNative
pid = tlsIdentity.pid;
}
- uid = UserId.getAppId(uid);
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -4921,13 +5087,14 @@ 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 +5106,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 +5398,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 +5734,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 +5764,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 +5772,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 +5800,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 +5871,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 +6215,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)
@@ -6203,7 +6374,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
- String name, IBinder token, boolean stable) {
+ String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
@@ -6218,10 +6389,13 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
+ if (r.userId != userId) {
+ throw new SecurityException("Calling requested user " + userId
+ + " but app is user " + r.userId);
+ }
}
// First check if this content provider has been published...
- int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid());
cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
@@ -6296,6 +6470,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Binder.restoreCallingIdentity(origId);
}
+ boolean singleton;
if (!providerRunning) {
try {
cpi = AppGlobals.getPackageManager().
@@ -6306,7 +6481,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);
@@ -6325,6 +6502,16 @@ public final class ActivityManagerService extends ActivityManagerNative
"Attempt to launch content provider before system ready");
}
+ // Make sure that the user who owns this provider is started. If not,
+ // we don't want to allow it to run.
+ if (mStartedUsers.get(userId) == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": user " + userId + " is stopped");
+ return null;
+ }
+
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
final boolean firstClass = cpr == null;
@@ -6341,7 +6528,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.
}
@@ -6463,17 +6650,19 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- return getContentProviderImpl(caller, name, null, stable);
+ return getContentProviderImpl(caller, name, null, stable,
+ UserHandle.getCallingUserId());
}
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call getContentProviderExternal()");
- return getContentProviderExternalUnchecked(name, token);
+ return getContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
}
- private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
- return getContentProviderImpl(null, name, token, true);
+ private ContentProviderHolder getContentProviderExternalUnchecked(String name,
+ IBinder token, int userId) {
+ return getContentProviderImpl(null, name, token, true, userId);
}
/**
@@ -6504,13 +6693,12 @@ public final class ActivityManagerService extends ActivityManagerNative
public void removeContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call removeContentProviderExternal()");
- removeContentProviderExternalUnchecked(name, token);
+ removeContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
}
- private void removeContentProviderExternalUnchecked(String name, IBinder token) {
+ private void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
synchronized (this) {
- ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
- Binder.getOrigCallingUser());
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6519,8 +6707,7 @@ public final class ActivityManagerService extends ActivityManagerNative
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
- Binder.getOrigCallingUser());
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
if (localCpr.hasExternalProcessHandles()) {
if (localCpr.removeExternalProcessHandleLocked(token)) {
updateOomAdjLocked();
@@ -6731,14 +6918,16 @@ public final class ActivityManagerService extends ActivityManagerNative
* Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
* src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
*/
- public String getProviderMimeType(Uri uri) {
+ public String getProviderMimeType(Uri uri, int userId) {
enforceNotIsolatedCaller("getProviderMimeType");
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, true, "getProviderMimeType", null);
final String name = uri.getAuthority();
final long ident = Binder.clearCallingIdentity();
ContentProviderHolder holder = null;
try {
- holder = getContentProviderExternalUnchecked(name, null);
+ holder = getContentProviderExternalUnchecked(name, null, userId);
if (holder != null) {
return holder.provider.getType(uri);
}
@@ -6747,7 +6936,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
} finally {
if (holder != null) {
- removeContentProviderExternalUnchecked(name, null);
+ removeContentProviderExternalUnchecked(name, null, userId);
}
Binder.restoreCallingIdentity(ident);
}
@@ -6766,7 +6955,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 +6963,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 +7001,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 "
@@ -6843,7 +7032,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (count > 1) {
final long origId = Binder.clearCallingIdentity();
mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1),
- count-1, Activity.RESULT_CANCELED, null, "unhandled-back");
+ count-1, Activity.RESULT_CANCELED, null, "unhandled-back", true);
Binder.restoreCallingIdentity(origId);
}
}
@@ -6851,8 +7040,9 @@ public final class ActivityManagerService extends ActivityManagerNative
public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
+ final int userId = UserHandle.getCallingUserId();
String name = uri.getAuthority();
- ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null);
+ ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null, userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
// We record the binder invoker's uid in thread-local storage before
@@ -6874,7 +7064,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// We've got the fd now, so we're done with the provider.
- removeContentProviderExternalUnchecked(name, null);
+ removeContentProviderExternalUnchecked(name, null, userId);
} else {
Slog.d(TAG, "Failed to get provider for authority '" + name + "'");
}
@@ -7085,7 +7275,8 @@ public final class ActivityManagerService extends ActivityManagerNative
mDebugTransient = !persistent;
if (packageName != null) {
final long origId = Binder.clearCallingIdentity();
- forceStopPackageLocked(packageName, -1, false, false, true, true, 0);
+ forceStopPackageLocked(packageName, -1, false, false, true, true,
+ UserHandle.USER_ALL);
Binder.restoreCallingIdentity(origId);
}
}
@@ -7219,7 +7410,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;
@@ -7504,42 +7695,45 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ final int[] users = getUsersLocked();
for (int i=0; i<ris.size(); i++) {
ActivityInfo ai = ris.get(i).activityInfo;
ComponentName comp = new ComponentName(ai.packageName, ai.name);
doneReceivers.add(comp);
intent.setComponent(comp);
- IIntentReceiver finisher = null;
- if (i == ris.size()-1) {
- finisher = new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky) {
- // The raw IIntentReceiver interface is called
- // with the AM lock held, so redispatch to
- // execute our code without the lock.
- mHandler.post(new Runnable() {
- public void run() {
- synchronized (ActivityManagerService.this) {
- mDidUpdate = true;
+ for (int j=0; j<users.length; j++) {
+ IIntentReceiver finisher = null;
+ if (i == ris.size()-1 && j == users.length-1) {
+ finisher = new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ // The raw IIntentReceiver interface is called
+ // with the AM lock held, so redispatch to
+ // execute our code without the lock.
+ mHandler.post(new Runnable() {
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ mDidUpdate = true;
+ }
+ writeLastDonePreBootReceivers(doneReceivers);
+ showBootMessage(mContext.getText(
+ R.string.android_upgrading_complete),
+ false);
+ systemReady(goingCallback);
}
- writeLastDonePreBootReceivers(doneReceivers);
- showBootMessage(mContext.getText(
- R.string.android_upgrading_complete),
- false);
- systemReady(goingCallback);
- }
- });
- }
- };
- }
- Slog.i(TAG, "Sending system update to: " + intent.getComponent());
- /* TODO: Send this to all users */
- broadcastIntentLocked(null, null, intent, null, finisher,
- 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
- 0 /* UserId zero */);
- if (finisher != null) {
- mWaitingUpdate = true;
+ });
+ }
+ };
+ }
+ Slog.i(TAG, "Sending system update to " + intent.getComponent()
+ + " for user " + users[j]);
+ broadcastIntentLocked(null, null, intent, null, finisher,
+ 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+ users[j]);
+ if (finisher != null) {
+ mWaitingUpdate = true;
+ }
}
}
}
@@ -7661,7 +7855,19 @@ public final class ActivityManagerService extends ActivityManagerNative
} catch (RemoteException e) {
}
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
mMainStack.resumeTopActivityLocked(null);
+ sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
}
}
@@ -7760,7 +7966,8 @@ public final class ActivityManagerService extends ActivityManagerNative
if (r.app == app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
+ r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "crashed", false);
}
}
if (!app.persistent) {
@@ -7795,7 +8002,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ r.intent.getComponent().flattenToShortString());
int index = mMainStack.indexOfActivityLocked(r);
r.stack.finishActivityLocked(r, index,
- Activity.RESULT_CANCELED, null, "crashed");
+ Activity.RESULT_CANCELED, null, "crashed", false);
// Also terminate any activities below it that aren't yet
// stopped, to avoid a situation where one will get
// re-start our crashing activity once it gets resumed again.
@@ -7809,7 +8016,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
r.stack.finishActivityLocked(r, index,
- Activity.RESULT_CANCELED, null, "crashed");
+ Activity.RESULT_CANCELED, null, "crashed", false);
}
}
}
@@ -8150,7 +8357,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (String pkg : process.pkgList) {
sb.append("Package: ").append(pkg);
try {
- PackageInfo pi = pm.getPackageInfo(pkg, 0, 0);
+ PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
if (pi != null) {
sb.append(" v").append(pi.versionCode);
if (pi.versionName != null) {
@@ -8423,11 +8630,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 +8706,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 +8719,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 +8775,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 +8948,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 +8969,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 +9008,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(" ");
}
@@ -9058,6 +9283,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
pw.println();
+ pw.println(" mStartedUsers:");
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
+ pw.print(": "); uss.dump("", pw);
+ }
+ pw.print(" mUserLru: [");
+ for (int i=0; i<mUserLru.size(); i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mUserLru.get(i));
+ }
+ pw.println("]");
pw.println(" mHomeProcess: " + mHomeProcess);
pw.println(" mPreviousProcess: " + mPreviousProcess);
if (dumpAll) {
@@ -9134,7 +9371,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 +9453,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
@@ -9527,9 +9652,15 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
-
+ boolean onlyHistory = false;
+
+ if ("history".equals(dumpPackage)) {
+ onlyHistory = true;
+ dumpPackage = null;
+ }
+
pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
- if (dumpAll) {
+ if (!onlyHistory && dumpAll) {
if (mRegisteredReceivers.size() > 0) {
boolean printed = false;
Iterator it = mRegisteredReceivers.values().iterator();
@@ -9562,39 +9693,41 @@ public final class ActivityManagerService extends ActivityManagerNative
needSep = true;
- if (mStickyBroadcasts != null && dumpPackage == null) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Sticky broadcasts:");
- StringBuilder sb = new StringBuilder(128);
- for (Map.Entry<String, ArrayList<Intent>> ent
- : mStickyBroadcasts.entrySet()) {
- pw.print(" * Sticky action "); pw.print(ent.getKey());
- if (dumpAll) {
- pw.println(":");
- ArrayList<Intent> intents = ent.getValue();
- final int N = intents.size();
- for (int i=0; i<N; i++) {
- sb.setLength(0);
- sb.append(" Intent: ");
- intents.get(i).toShortString(sb, false, true, false, false);
- pw.println(sb.toString());
- Bundle bundle = intents.get(i).getExtras();
- if (bundle != null) {
- pw.print(" ");
- pw.println(bundle.toString());
+ if (!onlyHistory && mStickyBroadcasts != null && dumpPackage == null) {
+ for (int user=0; user<mStickyBroadcasts.size(); user++) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.print(" Sticky broadcasts for user ");
+ pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":");
+ StringBuilder sb = new StringBuilder(128);
+ for (Map.Entry<String, ArrayList<Intent>> ent
+ : mStickyBroadcasts.valueAt(user).entrySet()) {
+ pw.print(" * Sticky action "); pw.print(ent.getKey());
+ if (dumpAll) {
+ pw.println(":");
+ ArrayList<Intent> intents = ent.getValue();
+ final int N = intents.size();
+ for (int i=0; i<N; i++) {
+ sb.setLength(0);
+ sb.append(" Intent: ");
+ intents.get(i).toShortString(sb, false, true, false, false);
+ pw.println(sb.toString());
+ Bundle bundle = intents.get(i).getExtras();
+ if (bundle != null) {
+ pw.print(" ");
+ pw.println(bundle.toString());
+ }
}
+ } else {
+ pw.println("");
}
- } else {
- pw.println("");
}
}
- needSep = true;
}
- if (dumpAll) {
+ if (!onlyHistory && dumpAll) {
pw.println();
for (BroadcastQueue queue : mBroadcastQueues) {
pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]="
@@ -9608,199 +9741,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 +10046,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 +10535,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 +10544,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 +10632,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.hasShownUi = false;
app.hasAboveClient = false;
- killServicesLocked(app, allowRestart);
+ mServices.killServicesLocked(app, allowRestart);
boolean restart = false;
@@ -10909,8 +10731,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
@@ -10974,806 +10797,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) {
@@ -11785,74 +10825,41 @@ 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;
}
}
ComponentName startServiceInPackage(int uid,
- Intent service, String resolvedType) {
+ Intent service, String resolvedType, int userId) {
synchronized(this) {
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, userId);
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) {
@@ -11861,149 +10868,111 @@ 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);
+ mServices.setServiceForegroundLocked(className, token, id, notification,
+ removeNotification);
+ }
+ }
+
+ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) {
+ synchronized(this) {
+ return handleIncomingUserLocked(callingPid, callingUid, userId, allowAll,
+ requireFull, name, callerPackage);
+ }
+ }
+
+ int handleIncomingUserLocked(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (callingUserId != userId) {
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ if ((requireFull || checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED)
+ && checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ callingPid, callingUid, -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (userId == UserHandle.USER_CURRENT_OR_SELF) {
+ // In this case, they would like to just execute as their
+ // owner user instead of failing.
+ userId = callingUserId;
+ } else {
+ StringBuilder builder = new StringBuilder(128);
+ builder.append("Permission Denial: ");
+ builder.append(name);
+ if (callerPackage != null) {
+ builder.append(" from ");
+ builder.append(callerPackage);
}
- }
- if (removeNotification) {
- r.cancelNotification();
- r.foregroundId = 0;
- r.foregroundNoti = null;
+ builder.append(" asks to run as user ");
+ builder.append(userId);
+ builder.append(" but is calling from user ");
+ builder.append(UserHandle.getUserId(callingUid));
+ builder.append("; this requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (!requireFull) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+ String msg = builder.toString();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
}
}
- }
- } 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 (userId == UserHandle.USER_CURRENT
+ || userId == UserHandle.USER_CURRENT_OR_SELF) {
+ userId = mCurrentUserId;
}
- }
- if (anyForeground != proc.foregroundServices) {
- proc.foregroundServices = anyForeground;
- if (oomAdj) {
- updateOomAdjLocked();
+ if (!allowAll && userId < 0) {
+ throw new IllegalArgumentException(
+ "Call does not support special user #" + userId);
}
}
+ return userId;
}
- 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;
}
@@ -12017,239 +10986,16 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- 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) {
@@ -12262,53 +11008,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);
}
}
@@ -12319,38 +11019,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);
}
}
@@ -12359,137 +11028,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);
}
}
@@ -12515,7 +11054,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 "
@@ -12633,9 +11172,13 @@ public final class ActivityManagerService extends ActivityManagerNative
// =========================================================
private final List getStickiesLocked(String action, IntentFilter filter,
- List cur) {
+ List cur, int userId) {
final ContentResolver resolver = mContext.getContentResolver();
- final ArrayList<Intent> list = mStickyBroadcasts.get(action);
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ if (stickies == null) {
+ return cur;
+ }
+ final ArrayList<Intent> list = stickies.get(action);
if (list == null) {
return cur;
}
@@ -12674,8 +11217,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
- IIntentReceiver receiver, IntentFilter filter, String permission) {
+ IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
+ int callingUid;
+ int callingPid;
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) {
@@ -12691,10 +11236,17 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
+ callingUid = callerApp.info.uid;
+ callingPid = callerApp.pid;
} else {
callerPackage = null;
+ callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
}
+ userId = this.handleIncomingUserLocked(callingPid, callingUid, userId,
+ true, true, "registerReceiver", callerPackage);
+
List allSticky = null;
// Look for any matching sticky broadcasts...
@@ -12702,10 +11254,16 @@ public final class ActivityManagerService extends ActivityManagerNative
if (actions != null) {
while (actions.hasNext()) {
String action = (String)actions.next();
- allSticky = getStickiesLocked(action, filter, allSticky);
+ allSticky = getStickiesLocked(action, filter, allSticky,
+ UserHandle.USER_ALL);
+ allSticky = getStickiesLocked(action, filter, allSticky,
+ UserHandle.getUserId(callingUid));
}
} else {
- allSticky = getStickiesLocked(null, filter, allSticky);
+ allSticky = getStickiesLocked(null, filter, allSticky,
+ UserHandle.USER_ALL);
+ allSticky = getStickiesLocked(null, filter, allSticky,
+ UserHandle.getUserId(callingUid));
}
// The first sticky in the list is returned directly back to
@@ -12722,9 +11280,8 @@ public final class ActivityManagerService extends ActivityManagerNative
ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
- rl = new ReceiverList(this, callerApp,
- Binder.getCallingPid(),
- Binder.getCallingUid(), receiver);
+ rl = new ReceiverList(this, callerApp, callingPid, callingUid,
+ userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
@@ -12736,8 +11293,21 @@ public final class ActivityManagerService extends ActivityManagerNative
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
+ } else if (rl.uid != callingUid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for uid " + callingUid
+ + " was previously registered for uid " + rl.uid);
+ } else if (rl.pid != callingPid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for pid " + callingPid
+ + " was previously registered for pid " + rl.pid);
+ } else if (rl.userId != userId) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for user " + userId
+ + " was previously registered for user " + rl.userId);
}
- BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission);
+ BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
+ permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
@@ -12756,7 +11326,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, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
@@ -12819,10 +11389,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private final void sendPackageBroadcastLocked(int cmd, String[] packages) {
+ private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
+ if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
try {
r.thread.dispatchPackageBroadcast(cmd, packages);
} catch (RemoteException ex) {
@@ -12830,7 +11400,67 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
-
+
+ private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
+ int[] users) {
+ List<ResolveInfo> receivers = null;
+ try {
+ HashSet<ComponentName> singleUserReceivers = null;
+ boolean scannedFirstReceivers = false;
+ for (int user : users) {
+ List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
+ .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
+ if (newReceivers != null && newReceivers.size() == 0) {
+ newReceivers = null;
+ }
+ if (receivers == null) {
+ receivers = newReceivers;
+ } else if (newReceivers != null) {
+ // We need to concatenate the additional receivers
+ // found with what we have do far. This would be easy,
+ // but we also need to de-dup any receivers that are
+ // singleUser.
+ if (!scannedFirstReceivers) {
+ // Collect any single user receivers we had already retrieved.
+ scannedFirstReceivers = true;
+ for (int i=0; i<receivers.size(); i++) {
+ ResolveInfo ri = receivers.get(i);
+ if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ ComponentName cn = new ComponentName(
+ ri.activityInfo.packageName, ri.activityInfo.name);
+ if (singleUserReceivers == null) {
+ singleUserReceivers = new HashSet<ComponentName>();
+ }
+ singleUserReceivers.add(cn);
+ }
+ }
+ }
+ // Add the new results to the existing results, tracking
+ // and de-dupping single user receivers.
+ for (int i=0; i<newReceivers.size(); i++) {
+ ResolveInfo ri = newReceivers.get(i);
+ if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ ComponentName cn = new ComponentName(
+ ri.activityInfo.packageName, ri.activityInfo.name);
+ if (singleUserReceivers == null) {
+ singleUserReceivers = new HashSet<ComponentName>();
+ }
+ if (!singleUserReceivers.contains(cn)) {
+ singleUserReceivers.add(cn);
+ receivers.add(ri);
+ }
+ } else {
+ receivers.add(ri);
+ }
+ }
+ }
+ }
+ } catch (RemoteException ex) {
+ // pm is in same process, this will never happen.
+ }
+ return receivers;
+ }
+
private final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
@@ -12848,7 +11478,45 @@ public final class ActivityManagerService extends ActivityManagerNative
if ((resultTo != null) && !ordered) {
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
-
+
+ userId = handleIncomingUserLocked(callingPid, callingUid, userId,
+ true, false, "broadcast", callerPackage);
+
+ // Make sure that the user who is receiving this broadcast is started
+ // If not, we will just skip it.
+ if (userId != UserHandle.USER_ALL && mStartedUsers.get(userId) == null) {
+ if (callingUid != Process.SYSTEM_UID || (intent.getFlags()
+ & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
+ Slog.w(TAG, "Skipping broadcast of " + intent
+ + ": user " + userId + " is stopped");
+ return ActivityManager.BROADCAST_SUCCESS;
+ }
+ }
+
+ /*
+ * Prevent non-system code (defined here to be non-persistent
+ * processes) from sending protected broadcasts.
+ */
+ if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
+ || callingUid == Process.SHELL_UID || callingUid == Process.BLUETOOTH_UID ||
+ callingUid == 0) {
+ // Always okay.
+ } else if (callerApp == null || !callerApp.persistent) {
+ try {
+ if (AppGlobals.getPackageManager().isProtectedBroadcast(
+ intent.getAction())) {
+ String msg = "Permission Denial: not allowed to send broadcast "
+ + intent.getAction() + " from pid="
+ + callingPid + ", uid=" + callingUid;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception", e);
+ return ActivityManager.BROADCAST_SUCCESS;
+ }
+ }
+
// 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.
@@ -12873,7 +11541,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
} else {
- // If resources are unvailble just force stop all
+ // If resources are unavailable just force stop all
// those packages and flush the attribute cache as well.
if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -12882,7 +11550,7 @@ public final class ActivityManagerService extends ActivityManagerNative
forceStopPackageLocked(pkg, -1, false, true, true, false, userId);
}
sendPackageBroadcastLocked(
- IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list);
+ IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, userId);
}
} else {
Uri data = intent.getData();
@@ -12895,7 +11563,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
- new String[] {ssp});
+ new String[] {ssp}, userId);
}
}
}
@@ -12939,29 +11607,6 @@ public final class ActivityManagerService extends ActivityManagerNative
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy));
}
- /*
- * Prevent non-system code (defined here to be non-persistent
- * processes) from sending protected broadcasts.
- */
- if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
- || callingUid == Process.SHELL_UID || callingUid == 0) {
- // Always okay.
- } else if (callerApp == null || !callerApp.persistent) {
- try {
- if (AppGlobals.getPackageManager().isProtectedBroadcast(
- intent.getAction())) {
- String msg = "Permission Denial: not allowed to send broadcast "
- + intent.getAction() + " from pid="
- + callingPid + ", uid=" + callingUid;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Remote exception", e);
- return ActivityManager.BROADCAST_SUCCESS;
- }
- }
-
// Add to the sticky list if requested.
if (sticky) {
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
@@ -12982,10 +11627,38 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(
"Sticky broadcasts can't target a specific component");
}
- ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
+ // We use userId directly here, since the "all" target is maintained
+ // as a separate set of sticky broadcasts.
+ if (userId != UserHandle.USER_ALL) {
+ // But first, if this is not a broadcast to all users, then
+ // make sure it doesn't conflict with an existing broadcast to
+ // all users.
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
+ UserHandle.USER_ALL);
+ if (stickies != null) {
+ ArrayList<Intent> list = stickies.get(intent.getAction());
+ if (list != null) {
+ int N = list.size();
+ int i;
+ for (i=0; i<N; i++) {
+ if (intent.filterEquals(list.get(i))) {
+ throw new IllegalArgumentException(
+ "Sticky broadcast " + intent + " for user "
+ + userId + " conflicts with existing global broadcast");
+ }
+ }
+ }
+ }
+ }
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ if (stickies == null) {
+ stickies = new HashMap<String, ArrayList<Intent>>();
+ mStickyBroadcasts.put(userId, stickies);
+ }
+ ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {
list = new ArrayList<Intent>();
- mStickyBroadcasts.put(intent.getAction(), list);
+ stickies.put(intent.getAction(), list);
}
int N = list.size();
int i;
@@ -13001,37 +11674,29 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ int[] users;
+ if (userId == UserHandle.USER_ALL) {
+ // Caller wants broadcast to go to all started users.
+ users = new int[mStartedUsers.size()];
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ users[i] = mStartedUsers.keyAt(i);
+ }
+ } else {
+ // Caller wants broadcast to go to one specific user.
+ users = new int[] {userId};
+ }
+
// Figure out who all will receive this broadcast.
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);
- }
- } catch (RemoteException ex) {
- // pm is in same process, this will never happen.
+ // Need to resolve the intent to interested receivers...
+ if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ == 0) {
+ receivers = collectReceiverComponents(intent, resolvedType, users);
+ }
+ if (intent.getComponent() == null) {
+ registeredReceivers = mReceiverResolver.queryIntent(intent,
+ resolvedType, false, userId);
}
final boolean replacePending =
@@ -13049,7 +11714,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, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
@@ -13139,7 +11804,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, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
@@ -13224,13 +11889,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ userId = handleIncomingUserLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, true, false, "removeStickyBroadcast", null);
+
synchronized(this) {
if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
!= PackageManager.PERMISSION_GRANTED) {
@@ -13241,15 +11908,24 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
- if (list != null) {
- int N = list.size();
- int i;
- for (i=0; i<N; i++) {
- if (intent.filterEquals(list.get(i))) {
- list.remove(i);
- break;
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ if (stickies != null) {
+ ArrayList<Intent> list = stickies.get(intent.getAction());
+ if (list != null) {
+ int N = list.size();
+ int i;
+ for (i=0; i<N; i++) {
+ if (intent.filterEquals(list.get(i))) {
+ list.remove(i);
+ break;
+ }
}
+ if (list.size() <= 0) {
+ stickies.remove(intent.getAction());
+ }
+ }
+ if (stickies.size() <= 0) {
+ mStickyBroadcasts.remove(userId);
}
}
}
@@ -13305,8 +11981,10 @@ public final class ActivityManagerService extends ActivityManagerNative
public boolean startInstrumentation(ComponentName className,
String profileFile, int flags, Bundle arguments,
- IInstrumentationWatcher watcher) {
+ IInstrumentationWatcher watcher, int userId) {
enforceNotIsolatedCaller("startInstrumentation");
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, true, "startInstrumentation", null);
// Refuse possible leaked file descriptors
if (arguments != null && arguments.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Bundle");
@@ -13318,9 +11996,10 @@ public final class ActivityManagerService extends ActivityManagerNative
try {
ii = mContext.getPackageManager().getInstrumentationInfo(
className, STOCK_PM_FLAGS);
- ai = mContext.getPackageManager().getApplicationInfo(
- ii.targetPackage, STOCK_PM_FLAGS);
+ ai = AppGlobals.getPackageManager().getApplicationInfo(
+ ii.targetPackage, STOCK_PM_FLAGS, userId);
} catch (PackageManager.NameNotFoundException e) {
+ } catch (RemoteException e) {
}
if (ii == null) {
reportStartInstrumentationFailure(watcher, className,
@@ -13347,7 +12026,6 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- int userId = UserId.getCallingUserId();
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId);
@@ -13405,12 +12083,12 @@ public final class ActivityManagerService extends ActivityManagerNative
app.instrumentationProfileFile = null;
app.instrumentationArguments = null;
- forceStopPackageLocked(app.processName, -1, false, false, true, true, app.userId);
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId);
}
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");
@@ -13580,12 +12258,12 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+ null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_LOCALE_CHANGED),
null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+ null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
}
}
@@ -13706,7 +12384,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i = start; i > finishTo; i--) {
ActivityRecord r = history.get(i);
mMainStack.requestFinishActivityLocked(r.appToken, resultCode, resultData,
- "navigate-up");
+ "navigate-up", true);
// Only return the supplied result for the first activity finished
resultCode = Activity.RESULT_CANCELED;
resultData = null;
@@ -13723,7 +12401,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);
@@ -13732,7 +12410,7 @@ public final class ActivityManagerService extends ActivityManagerNative
foundParentInTask = false;
}
mMainStack.requestFinishActivityLocked(parent.appToken, resultCode,
- resultData, "navigate-up");
+ resultData, "navigate-up", true);
}
}
Binder.restoreCallingIdentity(origId);
@@ -13775,14 +12453,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;
}
@@ -13790,7 +12469,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;
@@ -13807,6 +12486,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;
@@ -13817,12 +12497,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;
}
}
}
@@ -13831,6 +12514,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.
@@ -13846,6 +12530,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;
@@ -13867,21 +12552,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;
@@ -13898,6 +12575,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) {
@@ -13915,9 +12593,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.
@@ -14005,7 +12694,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.
@@ -14051,8 +12740,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
@@ -14068,7 +12765,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
@@ -14190,8 +12888,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) {
@@ -14604,9 +13310,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;
@@ -14616,7 +13323,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) {
@@ -14694,7 +13401,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;
@@ -14703,7 +13410,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) {
@@ -14731,34 +13439,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,
@@ -14766,8 +13493,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
@@ -14797,18 +13553,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;
@@ -15007,7 +13765,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mAutoStopProfiler = false;
}
- public boolean profileControl(String process, boolean start,
+ public boolean profileControl(String process, int userId, boolean start,
String path, ParcelFileDescriptor fd, int profileType) throws RemoteException {
try {
@@ -15026,22 +13784,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ProcessRecord proc = null;
if (process != null) {
- try {
- int pid = Integer.parseInt(process);
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(pid);
- }
- } catch (NumberFormatException e) {
- }
-
- if (proc == null) {
- HashMap<String, SparseArray<ProcessRecord>> all
- = mProcessNames.getMap();
- SparseArray<ProcessRecord> procs = all.get(process);
- if (procs != null && procs.size() > 0) {
- proc = procs.valueAt(0);
- }
- }
+ proc = findProcessLocked(process, userId, "profileControl");
}
if (start && (proc == null || proc.thread == null)) {
@@ -15085,7 +13828,40 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public boolean dumpHeap(String process, boolean managed,
+ private ProcessRecord findProcessLocked(String process, int userId, String callName) {
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, callName, null);
+ ProcessRecord proc = null;
+ try {
+ int pid = Integer.parseInt(process);
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ } catch (NumberFormatException e) {
+ }
+
+ if (proc == null) {
+ HashMap<String, SparseArray<ProcessRecord>> all
+ = mProcessNames.getMap();
+ SparseArray<ProcessRecord> procs = all.get(process);
+ if (procs != null && procs.size() > 0) {
+ proc = procs.valueAt(0);
+ if (userId != UserHandle.USER_ALL && proc.userId != userId) {
+ for (int i=1; i<procs.size(); i++) {
+ ProcessRecord thisProc = procs.valueAt(i);
+ if (thisProc.userId == userId) {
+ proc = thisProc;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return proc;
+ }
+
+ public boolean dumpHeap(String process, int userId, boolean managed,
String path, ParcelFileDescriptor fd) throws RemoteException {
try {
@@ -15102,24 +13878,7 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("null fd");
}
- ProcessRecord proc = null;
- try {
- int pid = Integer.parseInt(process);
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(pid);
- }
- } catch (NumberFormatException e) {
- }
-
- if (proc == null) {
- HashMap<String, SparseArray<ProcessRecord>> all
- = mProcessNames.getMap();
- SparseArray<ProcessRecord> procs = all.get(process);
- if (procs != null && procs.size() > 0) {
- proc = procs.valueAt(0);
- }
- }
-
+ ProcessRecord proc = findProcessLocked(process, userId, "dumpHeap");
if (proc == null || proc.thread == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
@@ -15167,101 +13926,392 @@ public final class ActivityManagerService extends ActivityManagerNative
// Multi-user methods
- private int mCurrentUserId;
- private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
-
+ @Override
public boolean switchUser(int userId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- Slog.e(TAG, "Trying to switch user from unauthorized app");
- return false;
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
- if (mCurrentUserId == userId)
- return true;
- synchronized (this) {
- // Check if user is already logged in, otherwise check if user exists first before
- // adding to the list of logged in users.
- if (mLoggedInUsers.indexOfKey(userId) < 0) {
- if (!userExists(userId)) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final int oldUserId = mCurrentUserId;
+ if (oldUserId == userId) {
+ return true;
+ }
+
+ final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
+ if (userInfo == null) {
+ Slog.w(TAG, "No user info for user #" + userId);
return false;
}
- mLoggedInUsers.append(userId, userId);
+
+ mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
+ R.anim.screen_user_enter);
+
+ // If the user we are switching to is not currently started, then
+ // we need to start it now.
+ if (mStartedUsers.get(userId) == null) {
+ mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
+ }
+
+ mCurrentUserId = userId;
+ final Integer userIdInt = Integer.valueOf(userId);
+ mUserLru.remove(userIdInt);
+ mUserLru.add(userIdInt);
+
+ final UserStartedState uss = mStartedUsers.get(userId);
+
+ mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
+ oldUserId, userId, uss));
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
+ oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
+
+ if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
+ if (userId != 0) {
+ intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ broadcastIntentLocked(null, null, intent, null,
+ new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ synchronized (ActivityManagerService.this) {
+ getUserManagerLocked().makeInitialized(userInfo.id);
+ }
+ }
+ }, 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+ userId);
+ } else {
+ getUserManagerLocked().makeInitialized(userInfo.id);
+ }
+ }
+
+ boolean haveActivities = mMainStack.switchUserLocked(userId, uss);
+ if (!haveActivities) {
+ startHomeActivityLocked(userId);
+ }
+
+ sendUserSwitchBroadcastsLocked(oldUserId, userId);
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
- mCurrentUserId = userId;
- boolean haveActivities = mMainStack.switchUser(userId);
- if (!haveActivities) {
- startHomeActivityLocked(userId);
+ return true;
+ }
+
+ void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Intent intent;
+ if (oldUserId >= 0) {
+ intent = new Intent(Intent.ACTION_USER_BACKGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, oldUserId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false, MY_PID, Process.SYSTEM_UID, oldUserId);
}
+ if (newUserId >= 0) {
+ intent = new Intent(Intent.ACTION_USER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false, MY_PID, Process.SYSTEM_UID, newUserId);
+ intent = new Intent(Intent.ACTION_USER_SWITCHED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ android.Manifest.permission.MANAGE_USERS,
+ false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ void dispatchUserSwitch(final UserStartedState uss, final int oldUserId,
+ final int newUserId) {
+ final int N = mUserSwitchObservers.beginBroadcast();
+ if (N > 0) {
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ int mCount = 0;
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ synchronized (ActivityManagerService.this) {
+ if (mCurUserSwitchCallback == this) {
+ mCount++;
+ if (mCount == N) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+ }
+ }
+ };
+ synchronized (this) {
+ mCurUserSwitchCallback = callback;
+ }
+ for (int i=0; i<N; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
+ newUserId, callback);
+ } catch (RemoteException e) {
+ }
+ }
+ } else {
+ synchronized (this) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
}
+ mUserSwitchObservers.finishBroadcast();
+ }
- // 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);
+ void timeoutUserSwitch(UserStartedState uss, int oldUserId, int newUserId) {
+ synchronized (this) {
+ Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
- return true;
+ void sendContinueUserSwitchLocked(UserStartedState uss, int oldUserId, int newUserId) {
+ mCurUserSwitchCallback = null;
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
+ oldUserId, newUserId, uss));
}
- @Override
- public UserInfo getCurrentUser() throws RemoteException {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- Slog.e(TAG, "Trying to get user from unauthorized app");
- return null;
+ void continueUserSwitch(UserStartedState uss, int oldUserId, int newUserId) {
+ final int N = mUserSwitchObservers.beginBroadcast();
+ for (int i=0; i<N; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId);
+ } catch (RemoteException e) {
+ }
+ }
+ mUserSwitchObservers.finishBroadcast();
+ synchronized (this) {
+ mWindowManager.stopFreezingScreen();
}
- return AppGlobals.getPackageManager().getUser(mCurrentUserId);
}
- private void onUserRemoved(Intent intent) {
- int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
- if (extraUserId < 1) return;
+ void finishUserSwitch(UserStartedState uss) {
+ synchronized (this) {
+ if (uss.mState == UserStartedState.STATE_BOOTING
+ && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
+ uss.mState = UserStartedState.STATE_RUNNING;
+ final int userId = uss.mHandle.getIdentifier();
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
+ }
+ }
+ }
- // Kill all the processes for the user
- ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>();
+ @Override
+ public int stopUser(final int userId, final IStopUserCallback callback) {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (userId <= 0) {
+ throw new IllegalArgumentException("Can't stop primary user " + userId);
+ }
synchronized (this) {
- HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap();
- 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) {
- pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
- }
+ if (mCurrentUserId == userId) {
+ return ActivityManager.USER_OP_IS_CURRENT;
+ }
+
+ final UserStartedState uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ // User is not started, nothing to do... but we do need to
+ // callback if requested.
+ if (callback != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.userStopped(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ });
}
+ return ActivityManager.USER_OP_SUCCESS;
}
- for (Pair<String,Integer> pkgAndUid : pkgAndUids) {
- forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second,
- false, false, true, true, extraUserId);
+ if (callback != null) {
+ uss.mStopCallbacks.add(callback);
+ }
+
+ if (uss.mState != UserStartedState.STATE_STOPPING) {
+ uss.mState = UserStartedState.STATE_STOPPING;
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // Inform of user switch
+ Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
+ final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ finishUserStop(uss);
+ }
+ };
+ broadcastIntentLocked(null, null, intent,
+ null, resultReceiver, 0, null, null, null,
+ true, false, MY_PID, Process.SYSTEM_UID, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
+
+ return ActivityManager.USER_OP_SUCCESS;
+ }
+
+ void finishUserStop(UserStartedState uss) {
+ final int userId = uss.mHandle.getIdentifier();
+ boolean stopped;
+ ArrayList<IStopUserCallback> callbacks;
+ synchronized (this) {
+ callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
+ if (uss.mState != UserStartedState.STATE_STOPPING
+ || mStartedUsers.get(userId) != uss) {
+ stopped = false;
+ } else {
+ stopped = true;
+ // User can no longer run.
+ mStartedUsers.remove(userId);
+
+ // Clean up all state and processes associated with the user.
+ // Kill all the processes for the user.
+ forceStopUserLocked(userId);
+ }
+ }
+
+ for (int i=0; i<callbacks.size(); i++) {
+ try {
+ if (stopped) callbacks.get(i).userStopped(userId);
+ else callbacks.get(i).userStopAborted(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public UserInfo getCurrentUser() {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: getCurrentUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ synchronized (this) {
+ return getUserManagerLocked().getUserInfo(mCurrentUserId);
+ }
+ }
+
+ @Override
+ public boolean isUserRunning(int userId) {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: isUserRunning() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ synchronized (this) {
+ return isUserRunningLocked(userId);
+ }
+ }
+
+ boolean isUserRunningLocked(int userId) {
+ UserStartedState state = mStartedUsers.get(userId);
+ return state != null && state.mState != UserStartedState.STATE_STOPPING;
+ }
+
+ @Override
+ public void registerUserSwitchObserver(IUserSwitchObserver observer) {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: registerUserSwitchObserver() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ mUserSwitchObservers.register(observer);
+ }
+
+ @Override
+ public void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
+ mUserSwitchObservers.unregister(observer);
}
private boolean userExists(int userId) {
- try {
- UserInfo user = AppGlobals.getPackageManager().getUser(userId);
- return user != null;
- } catch (RemoteException re) {
- // Won't happen, in same process
+ if (userId == 0) {
+ return true;
}
+ UserManagerService ums = getUserManagerLocked();
+ return ums != null ? (ums.getUserInfo(userId) != null) : false;
+ }
- return false;
+ int[] getUsersLocked() {
+ UserManagerService ums = getUserManagerLocked();
+ return ums != null ? ums.getUserIds() : new int[] { 0 };
+ }
+
+ UserManagerService getUserManagerLocked() {
+ if (mUserManager == null) {
+ IBinder b = ServiceManager.getService(Context.USER_SERVICE);
+ mUserManager = (UserManagerService)IUserManager.Stub.asInterface(b);
+ }
+ 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);
@@ -15272,7 +14322,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;
}
@@ -15280,86 +14330,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..009fb5d 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;
@@ -333,7 +333,6 @@ final class ActivityRecord {
state = ActivityState.INITIALIZING;
frontOfTask = false;
launchFailed = false;
- haveState = false;
stopped = false;
delayedResume = false;
finishing = false;
@@ -347,6 +346,11 @@ final class ActivityRecord {
idle = false;
hasBeenLaunched = false;
+ // This starts out true, since the initial state of an activity
+ // is that we have everything, and we shouldn't never consider it
+ // lacking in state to be removed if it dies.
+ haveState = true;
+
if (aInfo != null) {
if (aInfo.targetActivity == null
|| aInfo.launchMode == ActivityInfo.LAUNCH_MULTIPLE
@@ -614,14 +618,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 b9e63b7..29ee0bc 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -52,13 +52,13 @@ import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
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;
@@ -210,7 +210,10 @@ final class ActivityStack {
*/
final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
= new ArrayList<IActivityManager.WaitResult>();
-
+
+ final ArrayList<UserStartedState> mStartingUsers
+ = new ArrayList<UserStartedState>();
+
/**
* Set when the system is going to sleep, until we have
* successfully paused the current activity and released our wake lock.
@@ -418,11 +421,10 @@ final class ActivityStack {
}
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
- // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
- if (!r.finishing && r != notTop) {
+ if (!r.finishing && r != notTop && r.userId == mCurrentUser) {
return r;
}
i--;
@@ -431,11 +433,10 @@ final class ActivityStack {
}
final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
- // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
- if (!r.finishing && !r.delayedResume && r != notTop) {
+ if (!r.finishing && !r.delayedResume && r != notTop && r.userId == mCurrentUser) {
return r;
}
i--;
@@ -453,12 +454,12 @@ final class ActivityStack {
* @return Returns the HistoryRecord of the next activity on the stack.
*/
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
- // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
// Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)) {
+ if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)
+ && r.userId == mCurrentUser) {
return r;
}
i--;
@@ -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--) {
@@ -573,37 +574,36 @@ final class ActivityStack {
* Move the activities around in the stack to bring a user to the foreground.
* @return whether there are any activities for the specified user.
*/
- final boolean switchUser(int userId) {
- synchronized (mService) {
- mCurrentUser = userId;
+ final boolean switchUserLocked(int userId, UserStartedState uss) {
+ mCurrentUser = userId;
+ mStartingUsers.add(uss);
- // Only one activity? Nothing to do...
- if (mHistory.size() < 2)
- return false;
+ // Only one activity? Nothing to do...
+ if (mHistory.size() < 2)
+ return false;
- boolean haveActivities = false;
- // Check if the top activity is from the new user.
- ActivityRecord top = mHistory.get(mHistory.size() - 1);
- if (top.userId == userId) return true;
- // Otherwise, move the user's activities to the top.
- int N = mHistory.size();
- int i = 0;
- while (i < N) {
- ActivityRecord r = mHistory.get(i);
- if (r.userId == userId) {
- ActivityRecord moveToTop = mHistory.remove(i);
- mHistory.add(moveToTop);
- // No need to check the top one now
- N--;
- haveActivities = true;
- } else {
- i++;
- }
+ boolean haveActivities = false;
+ // Check if the top activity is from the new user.
+ ActivityRecord top = mHistory.get(mHistory.size() - 1);
+ if (top.userId == userId) return true;
+ // Otherwise, move the user's activities to the top.
+ int N = mHistory.size();
+ int i = 0;
+ while (i < N) {
+ ActivityRecord r = mHistory.get(i);
+ if (r.userId == userId) {
+ ActivityRecord moveToTop = mHistory.remove(i);
+ mHistory.add(moveToTop);
+ // No need to check the top one now
+ N--;
+ haveActivities = true;
+ } else {
+ i++;
}
- // Transition from the old top to the new top
- resumeTopActivityLocked(top);
- return haveActivities;
}
+ // Transition from the old top to the new top
+ resumeTopActivityLocked(top);
+ return haveActivities;
}
final boolean realStartActivityLocked(ActivityRecord r,
@@ -723,7 +723,7 @@ final class ActivityStack {
+ ", giving up", e);
mService.appDiedLocked(app, app.pid, app.thread);
requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "2nd-crash");
+ "2nd-crash", false);
return false;
}
@@ -754,8 +754,6 @@ final class ActivityStack {
completeResumeLocked(r);
checkReadyForSleepLocked();
if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle);
- r.icicle = null;
- r.haveState = false;
} else {
// This activity is not starting in the resumed state... which
// should look like we asked it to pause+stop (but remain visible),
@@ -902,7 +900,7 @@ final class ActivityStack {
mService.notifyAll();
}
}
-
+
public final Bitmap screenshotActivities(ActivityRecord who) {
if (who.noDisplay) {
return null;
@@ -919,7 +917,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;
}
@@ -1008,7 +1007,21 @@ final class ActivityStack {
resumeTopActivityLocked(null);
}
}
-
+
+ final void activityResumed(IBinder token) {
+ ActivityRecord r = null;
+
+ synchronized (mService) {
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ r = mHistory.get(index);
+ if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r);
+ r.icicle = null;
+ r.haveState = false;
+ }
+ }
+ }
+
final void activityPaused(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
@@ -1089,7 +1102,7 @@ final class ActivityStack {
if (prev != null) {
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
- prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
+ prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
} else if (prev.app != null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
if (prev.waitingVisible) {
@@ -1395,7 +1408,7 @@ final class ActivityStack {
// Launcher...
if (mMainStack) {
ActivityOptions.abort(options);
- return mService.startHomeActivityLocked(0);
+ return mService.startHomeActivityLocked(mCurrentUser);
}
}
@@ -1425,7 +1438,16 @@ final class ActivityStack {
ActivityOptions.abort(options);
return false;
}
-
+
+ // Make sure that the user who owns this activity is started. If not,
+ // we will just leave it as is because someone should be bringing
+ // another user's activities to the top of the stack.
+ if (mService.mStartedUsers.get(next.userId) == null) {
+ Slog.w(TAG, "Skipping resume of top activity " + next
+ + ": user " + next.userId + " is stopped");
+ return false;
+ }
+
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
mStoppingActivities.remove(next);
@@ -1477,6 +1499,15 @@ final class ActivityStack {
// can be resumed...
if (mResumedActivity != null) {
if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
+ // At this point we want to put the upcoming activity's process
+ // at the top of the LRU list, since we know we will be needing it
+ // very soon and it would be a waste to let it get killed if it
+ // happens to be sitting towards the end.
+ if (next.app != null && next.app.thread != null) {
+ // No reason to do full oom adj update here; we'll let that
+ // happen whenever it needs to later.
+ mService.updateLruProcessLocked(next.app, false, true);
+ }
startPausingLocked(userLeaving, false);
return true;
}
@@ -1492,7 +1523,7 @@ final class ActivityStack {
Slog.d(TAG, "no-history finish of " + last + " on new resume");
}
requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null,
- "no-history");
+ "no-history", false);
}
}
@@ -1714,14 +1745,9 @@ final class ActivityStack {
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
- "resume-exception");
+ "resume-exception", true);
return true;
}
-
- // Didn't need to use the icicle, and it is now out of date.
- if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; didn't need icicle of: " + next);
- next.icicle = null;
- next.haveState = false;
next.stopped = false;
} else {
@@ -2068,7 +2094,7 @@ final class ActivityStack {
continue;
}
if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
+ Activity.RESULT_CANCELED, null, "reset", false)) {
replyChainEnd--;
srcPos--;
}
@@ -2131,7 +2157,7 @@ final class ActivityStack {
continue;
}
if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
+ Activity.RESULT_CANCELED, null, "reset", false)) {
taskTopI--;
lastReparentPos--;
replyChainEnd--;
@@ -2188,7 +2214,7 @@ final class ActivityStack {
}
if (p.intent.getComponent().equals(target.intent.getComponent())) {
if (finishActivityLocked(p, j,
- Activity.RESULT_CANCELED, null, "replace")) {
+ Activity.RESULT_CANCELED, null, "replace", false)) {
taskTopI--;
lastReparentPos--;
}
@@ -2258,7 +2284,7 @@ final class ActivityStack {
continue;
}
if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
+ null, "clear", false)) {
i--;
}
}
@@ -2272,7 +2298,7 @@ final class ActivityStack {
int index = indexOfTokenLocked(ret.appToken);
if (index >= 0) {
finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
- null, "clear");
+ null, "clear", false);
}
return null;
}
@@ -2301,7 +2327,7 @@ final class ActivityStack {
continue;
}
if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
+ null, "clear", false)) {
i++;
}
}
@@ -2404,7 +2430,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));
}
@@ -3013,7 +3039,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);
@@ -3062,7 +3089,7 @@ final class ActivityStack {
IIntentSender target = mService.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, null, null, 0, new Intent[] { intent },
+ realCallingUid, userId, null, null, 0, new Intent[] { intent },
new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT, null);
@@ -3306,7 +3333,7 @@ final class ActivityStack {
Slog.d(TAG, "no-history finish of " + r);
}
requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "no-history");
+ "no-history", false);
} else {
if (DEBUG_STATES) Slog.d(TAG, "Not finishing noHistory " + r
+ " on stop because we're just sleeping");
@@ -3412,6 +3439,7 @@ final class ActivityStack {
ArrayList<ActivityRecord> stops = null;
ArrayList<ActivityRecord> finishes = null;
ArrayList<ActivityRecord> thumbnails = null;
+ ArrayList<UserStartedState> startingUsers = null;
int NS = 0;
int NF = 0;
int NT = 0;
@@ -3493,6 +3521,10 @@ final class ActivityStack {
booting = mService.mBooting;
mService.mBooting = false;
}
+ if (mStartingUsers.size() > 0) {
+ startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+ mStartingUsers.clear();
+ }
}
int i;
@@ -3513,7 +3545,7 @@ final class ActivityStack {
ActivityRecord r = (ActivityRecord)stops.get(i);
synchronized (mService) {
if (r.finishing) {
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
} else {
stopActivityLocked(r);
}
@@ -3537,6 +3569,10 @@ final class ActivityStack {
if (booting) {
mService.finishBooting();
+ } else if (startingUsers != null) {
+ for (i=0; i<startingUsers.size(); i++) {
+ mService.finishUserSwitch(startingUsers.get(i));
+ }
}
mService.trimApplications();
@@ -3559,7 +3595,7 @@ final class ActivityStack {
* some reason it is being left as-is.
*/
final boolean requestFinishActivityLocked(IBinder token, int resultCode,
- Intent resultData, String reason) {
+ Intent resultData, String reason, boolean oomAdj) {
int index = indexOfTokenLocked(token);
if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(
TAG, "Finishing activity @" + index + ": token=" + token
@@ -3570,7 +3606,7 @@ final class ActivityStack {
}
ActivityRecord r = mHistory.get(index);
- finishActivityLocked(r, index, resultCode, resultData, reason);
+ finishActivityLocked(r, index, resultCode, resultData, reason, oomAdj);
return true;
}
@@ -3587,10 +3623,11 @@ final class ActivityStack {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
finishActivityLocked(r, i,
- Activity.RESULT_CANCELED, null, "request-sub");
+ Activity.RESULT_CANCELED, null, "request-sub", false);
}
}
}
+ mService.updateOomAdjLocked();
}
final boolean finishActivityAffinityLocked(IBinder token) {
@@ -3613,7 +3650,8 @@ final class ActivityStack {
if (cur.taskAffinity != null && !cur.taskAffinity.equals(r.taskAffinity)) {
break;
}
- finishActivityLocked(cur, index, Activity.RESULT_CANCELED, null, "request-affinity");
+ finishActivityLocked(cur, index, Activity.RESULT_CANCELED, null,
+ "request-affinity", true);
index--;
}
return true;
@@ -3651,16 +3689,16 @@ final class ActivityStack {
* list, or false if it is still in the list and will be removed later.
*/
final boolean finishActivityLocked(ActivityRecord r, int index,
- int resultCode, Intent resultData, String reason) {
- return finishActivityLocked(r, index, resultCode, resultData, reason, false);
+ int resultCode, Intent resultData, String reason, boolean oomAdj) {
+ return finishActivityLocked(r, index, resultCode, resultData, reason, false, oomAdj);
}
/**
* @return Returns true if this activity has been removed from the history
* list, or false if it is still in the list and will be removed later.
*/
- final boolean finishActivityLocked(ActivityRecord r, int index,
- int resultCode, Intent resultData, String reason, boolean immediate) {
+ final boolean finishActivityLocked(ActivityRecord r, int index, int resultCode,
+ Intent resultData, String reason, boolean immediate, boolean oomAdj) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
@@ -3704,7 +3742,7 @@ final class ActivityStack {
if (immediate) {
return finishCurrentActivityLocked(r, index,
- FINISH_IMMEDIATELY) == null;
+ FINISH_IMMEDIATELY, oomAdj) == null;
} else if (mResumedActivity == r) {
boolean endTask = index <= 0
|| (mHistory.get(index-1)).task != r.task;
@@ -3728,7 +3766,7 @@ final class ActivityStack {
// it is done pausing; else we can just directly finish it here.
if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
return finishCurrentActivityLocked(r, index,
- FINISH_AFTER_PAUSE) == null;
+ FINISH_AFTER_PAUSE, oomAdj) == null;
} else {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
}
@@ -3741,17 +3779,17 @@ final class ActivityStack {
private static final int FINISH_AFTER_VISIBLE = 2;
private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int mode) {
+ int mode, boolean oomAdj) {
final int index = indexOfActivityLocked(r);
if (index < 0) {
return null;
}
- return finishCurrentActivityLocked(r, index, mode);
+ return finishCurrentActivityLocked(r, index, mode, oomAdj);
}
private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int index, int mode) {
+ int index, int mode, boolean oomAdj) {
// First things first: if this activity is currently visible,
// and the resumed activity is not yet visible, then hold off on
// finishing until the resumed one becomes visible.
@@ -3770,7 +3808,9 @@ final class ActivityStack {
if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r
+ " (finish requested)");
r.state = ActivityState.STOPPING;
- mService.updateOomAdjLocked();
+ if (oomAdj) {
+ mService.updateOomAdjLocked();
+ }
return r;
}
@@ -3790,7 +3830,8 @@ final class ActivityStack {
|| prevState == ActivityState.INITIALIZING) {
// If this activity is already stopped, we can just finish
// it right now.
- boolean activityRemoved = destroyActivityLocked(r, true, true, "finish-imm");
+ boolean activityRemoved = destroyActivityLocked(r, true,
+ oomAdj, "finish-imm");
if (activityRemoved) {
resumeTopActivityLocked(null);
}
@@ -3901,7 +3942,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;
}
@@ -3982,9 +4023,8 @@ final class ActivityStack {
ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
}
if (r.app.activities.size() == 0) {
- // No longer have activities, so update location in
- // LRU list.
- mService.updateLruProcessLocked(r.app, oomAdj, false);
+ // No longer have activities, so update oom adj.
+ mService.updateOomAdjLocked();
}
}
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..07440b5 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -27,13 +27,17 @@ class BroadcastFilter extends IntentFilter {
final ReceiverList receiverList;
final String packageName;
final String requiredPermission;
+ final int owningUid;
+ final int owningUserId;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _packageName, String _requiredPermission) {
+ String _packageName, String _requiredPermission, int _owningUid, int _userId) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
requiredPermission = _requiredPermission;
+ owningUid = _owningUid;
+ owningUserId = _userId;
}
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..b0af081 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;
@@ -219,7 +222,7 @@ public class BroadcastQueue {
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
- r.resultCode, r.resultData, r.resultExtras, r.ordered);
+ r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId);
if (DEBUG_BROADCAST) Slog.v(TAG,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -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());
}
@@ -354,15 +357,16 @@ public class BroadcastQueue {
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky) throws RemoteException {
+ boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null && app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky);
+ data, extras, ordered, sticky, sendingUser);
} else {
- receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
+ receiver.performReceive(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
}
}
@@ -382,7 +386,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) {
@@ -425,8 +429,8 @@ public class BroadcastQueue {
+ " (seq=" + seq + "): " + r);
}
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, r.ordered, r.initialSticky);
+ new Intent(r.intent), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
@@ -576,7 +580,7 @@ public class BroadcastQueue {
}
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false);
+ r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isnt kept in the mBroadcastHistory.
r.resultTo = null;
@@ -649,6 +653,9 @@ public class BroadcastQueue {
ResolveInfo info =
(ResolveInfo)nextReceiver;
+ ComponentName component = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
boolean skip = false;
int perm = mService.checkComponentPermission(info.activityInfo.permission,
@@ -661,16 +668,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 +691,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 +740,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 +754,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 "
@@ -945,12 +965,12 @@ public class BroadcastQueue {
if (!printed) {
if (needSep) {
pw.println();
- needSep = false;
}
+ needSep = true;
printed = true;
pw.println(" Active broadcasts [" + mQueueName + "]:");
}
- pw.println(" Broadcast #" + i + ":");
+ pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
br.dump(pw, " ");
}
printed = false;
@@ -965,9 +985,10 @@ public class BroadcastQueue {
pw.println();
}
needSep = true;
+ printed = true;
pw.println(" Active ordered broadcasts [" + mQueueName + "]:");
}
- pw.println(" Ordered Broadcast #" + i + ":");
+ pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":");
mOrderedBroadcasts.get(i).dump(pw, " ");
}
if (dumpPackage == null || (mPendingBroadcast != null
@@ -1003,7 +1024,8 @@ public class BroadcastQueue {
printed = true;
}
if (dumpAll) {
- pw.print(" Historical Broadcast #"); pw.print(i); pw.println(":");
+ pw.print(" Historical Broadcast " + mQueueName + " #");
+ pw.print(i); pw.println(":");
r.dump(pw, " ");
} else {
if (i >= 50) {
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index dd560fc..ca6d5f7 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 int userId; // user id this broadcast was for
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
@@ -79,7 +80,7 @@ class BroadcastRecord extends Binder {
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
- pw.print(prefix); pw.println(this);
+ pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
pw.print(prefix); pw.println(intent);
if (sticky) {
Bundle bundle = intent.getExtras();
@@ -140,14 +141,15 @@ class BroadcastRecord extends Binder {
pw.println(curReceiver.applicationInfo.sourceDir);
}
}
- String stateStr = " (?)";
- switch (state) {
- case IDLE: stateStr=" (IDLE)"; break;
- case APP_RECEIVE: stateStr=" (APP_RECEIVE)"; break;
- case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break;
- case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
+ if (state != IDLE) {
+ String stateStr = " (?)";
+ switch (state) {
+ case APP_RECEIVE: stateStr=" (APP_RECEIVE)"; break;
+ case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break;
+ case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
+ }
+ pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
}
- pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
final int N = receivers != null ? receivers.size() : 0;
String p2 = prefix + " ";
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
@@ -167,7 +169,8 @@ 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,
+ int _userId) {
queue = _queue;
intent = _intent;
callerApp = _callerApp;
@@ -183,6 +186,7 @@ class BroadcastRecord extends Binder {
ordered = _serialized;
sticky = _sticky;
initialSticky = _initialSticky;
+ userId = _userId;
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..de306b5 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;
}
@@ -82,7 +85,7 @@ class ContentProviderRecord {
public boolean canRunHere(ProcessRecord app) {
return (info.multiprocess || info.processName.equals(app.processName))
- && (uid == Process.SYSTEM_UID || uid == app.info.uid);
+ && uid == app.info.uid;
}
public void addExternalProcessHandleLocked(IBinder token) {
@@ -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..c61f13c 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -25,7 +25,6 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserId;
import android.util.Slog;
import java.io.PrintWriter;
@@ -54,11 +53,12 @@ class PendingIntentRecord extends IIntentSender.Stub {
String[] allResolvedTypes;
final int flags;
final int hashCode;
+ final int userId;
private static final int ODD_PRIME_NUMBER = 37;
Key(int _t, String _p, ActivityRecord _a, String _w,
- int _r, Intent[] _i, String[] _it, int _f, Bundle _o) {
+ int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) {
type = _t;
packageName = _p;
activity = _a;
@@ -70,10 +70,12 @@ class PendingIntentRecord extends IIntentSender.Stub {
allResolvedTypes = _it;
flags = _f;
options = _o;
-
+ userId = _userId;
+
int hash = 23;
hash = (ODD_PRIME_NUMBER*hash) + _f;
hash = (ODD_PRIME_NUMBER*hash) + _r;
+ hash = (ODD_PRIME_NUMBER*hash) + _userId;
if (_w != null) {
hash = (ODD_PRIME_NUMBER*hash) + _w.hashCode();
}
@@ -102,6 +104,9 @@ class PendingIntentRecord extends IIntentSender.Stub {
if (type != other.type) {
return false;
}
+ if (userId != other.userId){
+ return false;
+ }
if (!packageName.equals(other.packageName)) {
return false;
}
@@ -156,7 +161,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
+ " intent="
+ (requestIntent != null
? requestIntent.toShortString(false, true, false, false) : "<null>")
- + " flags=0x" + Integer.toHexString(flags) + "}";
+ + " flags=0x" + Integer.toHexString(flags) + " u=" + userId + "}";
}
String typeName() {
@@ -237,11 +242,10 @@ class PendingIntentRecord extends IIntentSender.Stub {
allIntents[allIntents.length-1] = finalIntent;
allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
owner.startActivitiesInPackage(uid, allIntents,
- allResolvedTypes, resultTo, options);
+ allResolvedTypes, resultTo, options, key.userId);
} else {
- owner.startActivityInPackage(uid,
- finalIntent, resolvedType,
- resultTo, resultWho, requestCode, 0, options);
+ owner.startActivityInPackage(uid, finalIntent, resolvedType,
+ resultTo, resultWho, requestCode, 0, options, key.userId);
}
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
@@ -259,8 +263,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false, UserId
- .getUserId(uid));
+ requiredPermission, (finishedReceiver != null), false, key.userId);
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
@@ -270,7 +273,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
case ActivityManager.INTENT_SENDER_SERVICE:
try {
owner.startServiceInPackage(uid,
- finalIntent, resolvedType);
+ finalIntent, resolvedType, key.userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Unable to send startService intent", e);
@@ -281,7 +284,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
if (sendFinish) {
try {
finishedReceiver.performReceive(new Intent(finalIntent), 0,
- null, null, false, false);
+ null, null, false, false, key.userId);
} catch (RemoteException 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..9dbf5f5 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -18,9 +18,8 @@ package com.android.server.am;
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;
@@ -31,8 +30,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
/**
* Keeps track of content providers by authority (name) and class. It separates the mapping by
@@ -44,9 +41,11 @@ public class ProviderMap {
private static final boolean DBG = false;
- private final HashMap<String, ContentProviderRecord> mGlobalByName
+ private final ActivityManagerService mAm;
+
+ 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
@@ -54,6 +53,10 @@ public class ProviderMap {
private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
= new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
+ ProviderMap(ActivityManagerService am) {
+ mAm = am;
+ }
+
ContentProviderRecord getProviderByName(String name) {
return getProviderByName(name, -1);
}
@@ -63,7 +66,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 +84,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 +98,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,56 +111,54 @@ 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)) {
+ void removeProviderByName(String name, int userId) {
+ 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 (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
if (DBG)
Slog.i(TAG,
- "Removing from providersByName name=" + name + " user="
- + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- HashMap<String, ContentProviderRecord> map = getProvidersByName(optionalUserId);
+ "Removing from providersByName name=" + name + " user=" + userId);
+ HashMap<String, ContentProviderRecord> map = getProvidersByName(userId);
// map returned by getProvidersByName wouldn't be null
map.remove(name);
if (map.size() == 0) {
- mProvidersByNamePerUser.remove(optionalUserId);
+ mProvidersByNamePerUser.remove(userId);
}
}
}
- void removeProviderByClass(ComponentName name, int optionalUserId) {
- if (mGlobalByClass.containsKey(name)) {
+ void removeProviderByClass(ComponentName name, int userId) {
+ if (mSingletonByClass.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByClass name=" + name);
- mGlobalByClass.remove(name);
+ mSingletonByClass.remove(name);
} else {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
if (DBG)
Slog.i(TAG,
- "Removing from providersByClass name=" + name + " user="
- + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(optionalUserId);
+ "Removing from providersByClass name=" + name + " user=" + userId);
+ HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(userId);
// map returned by getProvidersByClass wouldn't be null
map.remove(name);
if (map.size() == 0) {
- mProvidersByClassPerUser.remove(optionalUserId);
+ mProvidersByClassPerUser.remove(userId);
}
}
}
- private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
- final int userId = optionalUserId >= 0
- ? optionalUserId : Binder.getOrigCallingUser();
+ private HashMap<String, ContentProviderRecord> getProvidersByName(int userId) {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
if (map == null) {
HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
@@ -168,12 +169,13 @@ public class ProviderMap {
}
}
- HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
- final int userId = optionalUserId >= 0
- ? optionalUserId : Binder.getOrigCallingUser();
- final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+ HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int userId) {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
+ final HashMap<ComponentName, ContentProviderRecord> map
+ = mProvidersByClassPerUser.get(userId);
if (map == null) {
- HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+ HashMap<ComponentName, ContentProviderRecord> newMap
+ = new HashMap<ComponentName, ContentProviderRecord>();
mProvidersByClassPerUser.put(userId, newMap);
return newMap;
} else {
@@ -181,6 +183,53 @@ public class ProviderMap {
}
}
+ private boolean collectForceStopProvidersLocked(String name, int appId,
+ boolean doit, boolean evenPersistent, int userId,
+ HashMap<ComponentName, ContentProviderRecord> providers,
+ ArrayList<ContentProviderRecord> result) {
+ boolean didSomething = false;
+ for (ContentProviderRecord provider : providers.values()) {
+ if ((name == null || provider.info.packageName.equals(name))
+ && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
+ if (!doit) {
+ return true;
+ }
+ didSomething = true;
+ result.add(provider);
+ }
+ }
+ return didSomething;
+ }
+
+ boolean collectForceStopProviders(String name, int appId,
+ boolean doit, boolean evenPersistent, int userId,
+ ArrayList<ContentProviderRecord> result) {
+ boolean didSomething = collectForceStopProvidersLocked(name, appId, doit,
+ evenPersistent, userId, mSingletonByClass, result);
+ if (!doit && didSomething) {
+ return true;
+ }
+ if (userId == UserHandle.USER_ALL) {
+ for (int i=0; i<mProvidersByClassPerUser.size(); i++) {
+ if (collectForceStopProvidersLocked(name, appId, doit, evenPersistent,
+ userId, mProvidersByClassPerUser.valueAt(i), result)) {
+ if (!doit) {
+ return true;
+ }
+ didSomething = true;
+ }
+ }
+ } else {
+ HashMap<ComponentName, ContentProviderRecord> items
+ = getProvidersByClass(userId);
+ if (items != null) {
+ didSomething |= collectForceStopProvidersLocked(name, appId, doit,
+ evenPersistent, userId, items, result);
+ }
+ }
+ return didSomething;
+ }
+
private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
HashMap<ComponentName, ContentProviderRecord> map) {
Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
@@ -207,37 +256,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));
}
}
@@ -245,30 +286,33 @@ public class ProviderMap {
protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll) {
+ ArrayList<ContentProviderRecord> allProviders = new ArrayList<ContentProviderRecord>();
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
- if ("all".equals(name)) {
- synchronized (this) {
- for (ContentProviderRecord r1 : getProvidersByClass(-1).values()) {
- providers.add(r1);
- }
+ synchronized (mAm) {
+ allProviders.addAll(mSingletonByClass.values());
+ for (int i=0; i<mProvidersByClassPerUser.size(); i++) {
+ allProviders.addAll(mProvidersByClassPerUser.valueAt(i).values());
}
- } 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) {
+
+ if ("all".equals(name)) {
+ providers.addAll(allProviders);
+ } 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 (ContentProviderRecord r1 : getProvidersByClass(-1).values()) {
+ for (int i=0; i<allProviders.size(); i++) {
+ ContentProviderRecord r1 = allProviders.get(i);
if (componentName != null) {
if (r1.name.equals(componentName)) {
providers.add(r1);
@@ -306,7 +350,7 @@ public class ProviderMap {
private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
final ContentProviderRecord r, String[] args, boolean dumpAll) {
String innerPrefix = prefix + " ";
- synchronized (this) {
+ synchronized (mAm) {
pw.print(prefix); pw.print("PROVIDER ");
pw.print(r);
pw.print(" pid=");
@@ -338,6 +382,4 @@ public class ProviderMap {
}
}
}
-
-
}
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 32c24c6..9b6701e 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -39,18 +39,20 @@ class ReceiverList extends ArrayList<BroadcastFilter>
public final ProcessRecord app;
public final int pid;
public final int uid;
+ public final int userId;
BroadcastRecord curBroadcast = null;
boolean linkedToDeath = false;
String stringName;
ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
- int _pid, int _uid, IIntentReceiver _receiver) {
+ int _pid, int _uid, int _userId, IIntentReceiver _receiver) {
owner = _owner;
receiver = _receiver;
app = _app;
pid = _pid;
uid = _uid;
+ userId = _userId;
}
// Want object identity, not the array identity we are inheriting.
@@ -67,8 +69,9 @@ class ReceiverList extends ArrayList<BroadcastFilter>
}
void dumpLocal(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("app="); pw.print(app);
- pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.println(uid);
+ pw.print(prefix); pw.print("app="); pw.print(app.toShortString());
+ pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.print(uid);
+ pw.print(" user="); pw.println(userId);
if (curBroadcast != null || linkedToDeath) {
pw.print(prefix); pw.print("curBroadcast="); pw.print(curBroadcast);
pw.print(" linkedToDeath="); pw.println(linkedToDeath);
@@ -103,6 +106,8 @@ class ReceiverList extends ArrayList<BroadcastFilter>
sb.append((app != null ? app.processName : "(unknown name)"));
sb.append('/');
sb.append(uid);
+ sb.append("/u");
+ sb.append(userId);
sb.append((receiver.asBinder() instanceof Binder) ? " local:" : " remote:");
sb.append(Integer.toHexString(System.identityHashCode(receiver.asBinder())));
sb.append('}');
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 828eef7..7055fdc 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,
@@ -366,7 +370,7 @@ class ServiceRecord extends Binder {
try {
int[] outId = new int[1];
nm.enqueueNotificationInternal(localPackageName, appUid, appPid,
- null, localForegroundId, localForegroundNoti, outId);
+ null, localForegroundId, localForegroundNoti, outId, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for service", e);
@@ -395,7 +399,8 @@ class ServiceRecord extends Binder {
return;
}
try {
- inm.cancelNotification(localPackageName, localForegroundId);
+ inm.cancelNotificationWithTag(localPackageName, null,
+ localForegroundId, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
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/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java
new file mode 100644
index 0000000..3f3ed85
--- /dev/null
+++ b/services/java/com/android/server/am/UserStartedState.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.am;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.app.IStopUserCallback;
+import android.os.UserHandle;
+
+public class UserStartedState {
+ public final static int STATE_BOOTING = 0;
+ public final static int STATE_RUNNING = 1;
+ public final static int STATE_STOPPING = 2;
+
+ public final UserHandle mHandle;
+ public final ArrayList<IStopUserCallback> mStopCallbacks
+ = new ArrayList<IStopUserCallback>();
+
+ public int mState = STATE_BOOTING;
+
+ public UserStartedState(UserHandle handle, boolean initial) {
+ mHandle = handle;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mState="); pw.println(mState);
+ }
+}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 682ecf8..79fb458 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -43,6 +43,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -319,6 +320,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;
@@ -415,7 +418,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeList);
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
erroredList);
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
if (DBG) {
Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
activeList.size() + ", " + erroredList.size());
@@ -501,8 +504,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 +1143,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 +1173,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mSetDnsForwardersErrorState = new SetDnsForwardersErrorState();
addState(mSetDnsForwardersErrorState);
- mNotifyList = new ArrayList();
+ mNotifyList = new ArrayList<TetherInterfaceSM>();
setInitialState(mInitialState);
}
@@ -1362,8 +1370,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 +1388,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 +1431,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 +1580,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..abc1d32
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.os.Handler;
+
+import java.io.PrintWriter;
+
+/**
+ * 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><p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+abstract class DisplayAdapter {
+ private final DisplayManagerService.SyncRoot mSyncRoot;
+ private final Context mContext;
+ private final Handler mHandler;
+ private final Listener mListener;
+ private final String mName;
+
+ public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
+ public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
+ public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
+
+ public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener, String name) {
+ mSyncRoot = syncRoot;
+ mContext = context;
+ mHandler = handler;
+ mListener = listener;
+ mName = name;
+ }
+
+ /**
+ * Gets the object that the display adapter should synchronize on when handling
+ * calls that come in from outside of the display manager service.
+ */
+ public final DisplayManagerService.SyncRoot getSyncRoot() {
+ return mSyncRoot;
+ }
+
+ /**
+ * Gets the display adapter's context.
+ */
+ public final Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Gets a handler that the display adapter may use to post asynchronous messages.
+ */
+ public final Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * Gets the display adapter name for debugging purposes.
+ */
+ public final String getName() {
+ return mName;
+ }
+
+ /**
+ * Registers the display adapter with the display manager.
+ *
+ * The display adapter should register any built-in display devices as soon as possible.
+ * The boot process will wait for the default display to be registered.
+ * Other display devices can be registered dynamically later.
+ */
+ public void registerLocked() {
+ }
+
+ /**
+ * Dumps the local state of the display adapter.
+ */
+ public void dumpLocked(PrintWriter pw) {
+ }
+
+ /**
+ * Sends a display device event to the display adapter listener asynchronously.
+ */
+ protected final void sendDisplayDeviceEventLocked(
+ final DisplayDevice device, final int event) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayDeviceEvent(device, event);
+ }
+ });
+ }
+
+ /**
+ * Sends a request to perform traversals.
+ */
+ protected final void sendTraversalRequestLocked() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onTraversalRequested();
+ }
+ });
+ }
+
+ public interface Listener {
+ public void onDisplayDeviceEvent(DisplayDevice device, int event);
+ public void onTraversalRequested();
+ }
+}
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..f5aa3d4
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -0,0 +1,200 @@
+/*
+ * 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.graphics.Rect;
+import android.os.IBinder;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents a physical display device such as the built-in display
+ * an external monitor, or a WiFi display.
+ * <p>
+ * Display devices are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+abstract class DisplayDevice {
+ private final DisplayAdapter mDisplayAdapter;
+ private final IBinder mDisplayToken;
+
+ // The display device does not manage these properties itself, they are set by
+ // the display manager service. The display device shouldn't really be looking at these.
+ private int mCurrentLayerStack = -1;
+ private int mCurrentOrientation = -1;
+ private Rect mCurrentLayerStackRect;
+ private Rect mCurrentDisplayRect;
+
+ // The display device owns its surface, but it should only set it
+ // within a transaction from performTraversalInTransactionLocked.
+ private Surface mCurrentSurface;
+
+ public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken) {
+ mDisplayAdapter = displayAdapter;
+ mDisplayToken = displayToken;
+ }
+
+ /**
+ * Gets the display adapter that owns the display device.
+ *
+ * @return The display adapter.
+ */
+ public final DisplayAdapter getAdapterLocked() {
+ return mDisplayAdapter;
+ }
+
+ /**
+ * Gets the Surface Flinger display token for this display.
+ *
+ * @return The display token, or null if the display is not being managed
+ * by Surface Flinger.
+ */
+ public final IBinder getDisplayTokenLocked() {
+ return mDisplayToken;
+ }
+
+ /**
+ * Gets the name of the display device.
+ *
+ * @return The display device name.
+ */
+ public final String getNameLocked() {
+ return getDisplayDeviceInfoLocked().name;
+ }
+
+ /**
+ * Gets information about the display device.
+ *
+ * The information returned should not change between calls unless the display
+ * adapter sent a {@link DisplayAdapter#DISPLAY_DEVICE_EVENT_CHANGED} event and
+ * {@link #applyPendingDisplayDeviceInfoChangesLocked()} has been called to apply
+ * the pending changes.
+ *
+ * @return The display device info, which should be treated as immutable by the caller.
+ * The display device should allocate a new display device info object whenever
+ * the data changes.
+ */
+ public abstract DisplayDeviceInfo getDisplayDeviceInfoLocked();
+
+ /**
+ * Applies any pending changes to the observable state of the display device
+ * if the display adapter sent a {@link DisplayAdapter#DISPLAY_DEVICE_EVENT_CHANGED} event.
+ */
+ public void applyPendingDisplayDeviceInfoChangesLocked() {
+ }
+
+ /**
+ * Gives the display device a chance to update its properties while in a transaction.
+ */
+ public void performTraversalInTransactionLocked() {
+ }
+
+ /**
+ * Sets the display layer stack while in a transaction.
+ */
+ public final void setLayerStackInTransactionLocked(int layerStack) {
+ if (mCurrentLayerStack != layerStack) {
+ mCurrentLayerStack = layerStack;
+ Surface.setDisplayLayerStack(mDisplayToken, layerStack);
+ }
+ }
+
+ /**
+ * Sets the display projection while in a transaction.
+ *
+ * @param orientation defines the display's orientation
+ * @param layerStackRect defines which area of the window manager coordinate
+ * space will be used
+ * @param displayRect defines where on the display will layerStackRect be
+ * mapped to. displayRect is specified post-orientation, that is
+ * it uses the orientation seen by the end-user
+ */
+ public final void setProjectionInTransactionLocked(int orientation,
+ Rect layerStackRect, Rect displayRect) {
+ if (mCurrentOrientation != orientation
+ || mCurrentLayerStackRect == null
+ || !mCurrentLayerStackRect.equals(layerStackRect)
+ || mCurrentDisplayRect == null
+ || !mCurrentDisplayRect.equals(displayRect)) {
+ mCurrentOrientation = orientation;
+
+ if (mCurrentLayerStackRect == null) {
+ mCurrentLayerStackRect = new Rect();
+ }
+ mCurrentLayerStackRect.set(layerStackRect);
+
+ if (mCurrentDisplayRect == null) {
+ mCurrentDisplayRect = new Rect();
+ }
+ mCurrentDisplayRect.set(displayRect);
+
+ Surface.setDisplayProjection(mDisplayToken,
+ orientation, layerStackRect, displayRect);
+ }
+ }
+
+ /**
+ * Sets the display surface while in a transaction.
+ */
+ public final void setSurfaceInTransactionLocked(Surface surface) {
+ if (mCurrentSurface != surface) {
+ mCurrentSurface = surface;
+ Surface.setDisplaySurface(mDisplayToken, surface);
+ }
+ }
+
+ /**
+ * Populates the specified viewport object with orientation,
+ * physical and logical rects based on the display's current projection.
+ */
+ public final void populateViewportLocked(DisplayViewport viewport) {
+ viewport.orientation = mCurrentOrientation;
+
+ if (mCurrentLayerStackRect != null) {
+ viewport.logicalFrame.set(mCurrentLayerStackRect);
+ } else {
+ viewport.logicalFrame.setEmpty();
+ }
+
+ if (mCurrentDisplayRect != null) {
+ viewport.physicalFrame.set(mCurrentDisplayRect);
+ } else {
+ viewport.physicalFrame.setEmpty();
+ }
+
+ boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90
+ || mCurrentOrientation == Surface.ROTATION_270);
+ DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
+ viewport.deviceWidth = isRotated ? info.height : info.width;
+ viewport.deviceHeight = isRotated ? info.width : info.height;
+ }
+
+ /**
+ * Dumps the local state of the display device.
+ * Does not need to dump the display device info because that is already dumped elsewhere.
+ */
+ public void dumpLocked(PrintWriter pw) {
+ pw.println("mAdapter=" + mDisplayAdapter.getName());
+ pw.println("mDisplayToken=" + mDisplayToken);
+ pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
+ pw.println("mCurrentOrientation=" + mCurrentOrientation);
+ pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
+ pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
+ pw.println("mCurrentSurface=" + mCurrentSurface);
+ }
+}
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..f0cd0f5
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -0,0 +1,190 @@
+/*
+ * 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.util.DisplayMetrics;
+
+import libcore.util.Objects;
+
+/**
+ * Describes the characteristics of a physical display device.
+ */
+final class DisplayDeviceInfo {
+ /**
+ * Flag: Indicates that this display device should be considered the default display
+ * device of the system.
+ */
+ public static final int FLAG_DEFAULT_DISPLAY = 1 << 0;
+
+ /**
+ * Flag: Indicates that this display device can rotate to show contents in a
+ * different orientation. Otherwise the rotation is assumed to be fixed in the
+ * natural orientation and the display manager should transform the content to fit.
+ */
+ public static final int FLAG_SUPPORTS_ROTATION = 1 << 1;
+
+ /**
+ * Flag: Indicates that this display device can show secure surfaces.
+ */
+ public static final int FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT = 1 << 2;
+
+ /**
+ * Touch attachment: Display does not receive touch.
+ */
+ public static final int TOUCH_NONE = 0;
+
+ /**
+ * Touch attachment: Touch input is via the internal interface.
+ */
+ public static final int TOUCH_INTERNAL = 1;
+
+ /**
+ * Touch attachment: Touch input is via an external interface, such as USB.
+ */
+ public static final int TOUCH_EXTERNAL = 2;
+
+ /**
+ * 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;
+
+ /**
+ * The refresh rate of the display.
+ */
+ public float refreshRate;
+
+ /**
+ * The nominal apparent density of the display in DPI used for layout calculations.
+ * This density is sensitive to the viewing distance. A big TV and a tablet may have
+ * the same apparent density even though the pixels on the TV are much bigger than
+ * those on the tablet.
+ */
+ public int densityDpi;
+
+ /**
+ * The physical density of the display in DPI in the X direction.
+ * This density should specify the physical size of each pixel.
+ */
+ public float xDpi;
+
+ /**
+ * The physical density of the display in DPI in the X direction.
+ * This density should specify the physical size of each pixel.
+ */
+ public float yDpi;
+
+ /**
+ * Display flags.
+ */
+ public int flags;
+
+ /**
+ * The touch attachment, per {@link DisplayViewport#touch}.
+ */
+ public int touch;
+
+ public void setAssumedDensityForExternalDisplay(int width, int height) {
+ densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
+ // Technically, these values should be smaller than the apparent density
+ // but we don't know the physical size of the display.
+ xDpi = densityDpi;
+ yDpi = densityDpi;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof DisplayDeviceInfo && equals((DisplayDeviceInfo)o);
+ }
+
+ public boolean equals(DisplayDeviceInfo other) {
+ return other != null
+ && Objects.equal(name, other.name)
+ && width == other.width
+ && height == other.height
+ && refreshRate == other.refreshRate
+ && densityDpi == other.densityDpi
+ && xDpi == other.xDpi
+ && yDpi == other.yDpi
+ && flags == other.flags
+ && touch == other.touch;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
+ }
+
+ 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;
+ flags = other.flags;
+ touch = other.touch;
+ }
+
+ // For debugging purposes
+ @Override
+ public String toString() {
+ return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
+ + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
+ + ", touch " + touchToString(touch) + flagsToString(flags) + "}";
+ }
+
+ private static String touchToString(int touch) {
+ switch (touch) {
+ case TOUCH_NONE:
+ return "NONE";
+ case TOUCH_INTERNAL:
+ return "INTERNAL";
+ case TOUCH_EXTERNAL:
+ return "EXTERNAL";
+ default:
+ return Integer.toString(touch);
+ }
+ }
+
+ private static String flagsToString(int flags) {
+ StringBuilder msg = new StringBuilder();
+ if ((flags & FLAG_DEFAULT_DISPLAY) != 0) {
+ msg.append(", FLAG_DEFAULT_DISPLAY");
+ }
+ if ((flags & FLAG_SUPPORTS_ROTATION) != 0) {
+ msg.append(", FLAG_SUPPORTS_ROTATION");
+ }
+ if ((flags & FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT) != 0) {
+ msg.append(", FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT");
+ }
+ return msg.toString();
+ }
+}
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..39f2418
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -0,0 +1,888 @@
+/*
+ * 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 com.android.internal.util.IndentingPrintWriter;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.IDisplayManager;
+import android.hardware.display.IDisplayManagerCallback;
+import android.hardware.display.WifiDisplayStatus;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Manages attached displays.
+ * <p>
+ * The {@link DisplayManagerService} manages the global lifecycle of displays,
+ * decides how to configure logical displays based on the physical display devices currently
+ * attached, sends notifications to the system and to applications when the state
+ * changes, and so on.
+ * </p><p>
+ * The display manager service relies on a collection of {@link DisplayAdapter} components,
+ * for discovering and configuring physical display devices attached to the system.
+ * There are separate display adapters for each manner that devices are attached:
+ * one display adapter for built-in local displays, one for simulated non-functional
+ * displays when the system is headless, one for simulated overlay displays used for
+ * development, one for wifi displays, etc.
+ * </p><p>
+ * Display adapters are only weakly coupled to the display manager service.
+ * Display adapters communicate changes in display device state to the display manager
+ * service asynchronously via a {@link DisplayAdapter.Listener} registered
+ * by the display manager service. This separation of concerns is important for
+ * two main reasons. First, it neatly encapsulates the responsibilities of these
+ * two classes: display adapters handle individual display devices whereas
+ * the display manager service handles the global state. Second, it eliminates
+ * the potential for deadlocks resulting from asynchronous display device discovery.
+ * </p>
+ *
+ * <h3>Synchronization</h3>
+ * <p>
+ * Because the display manager may be accessed by multiple threads, the synchronization
+ * story gets a little complicated. In particular, the window manager may call into
+ * the display manager while holding a surface transaction with the expectation that
+ * it can apply changes immediately. Unfortunately, that means we can't just do
+ * everything asynchronously (*grump*).
+ * </p><p>
+ * To make this work, all of the objects that belong to the display manager must
+ * use the same lock. We call this lock the synchronization root and it has a unique
+ * type {@link DisplayManagerService.SyncRoot}. Methods that require this lock are
+ * named with the "Locked" suffix.
+ * </p><p>
+ * Where things get tricky is that the display manager is not allowed to make
+ * any potentially reentrant calls, especially into the window manager. We generally
+ * avoid this by making all potentially reentrant out-calls asynchronous.
+ * </p>
+ */
+public final class DisplayManagerService extends IDisplayManager.Stub {
+ private static final String TAG = "DisplayManagerService";
+ private static final boolean DEBUG = false;
+
+ private static final String SYSTEM_HEADLESS = "ro.config.headless";
+ private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
+
+ private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1;
+ private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
+ private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
+ private static final int MSG_REQUEST_TRAVERSAL = 4;
+ private static final int MSG_UPDATE_VIEWPORT = 5;
+
+ private final Context mContext;
+ private final boolean mHeadless;
+ private final DisplayManagerHandler mHandler;
+ private final Handler mUiHandler;
+ private final DisplayAdapterListener mDisplayAdapterListener;
+ private WindowManagerFuncs mWindowManagerFuncs;
+ private InputManagerFuncs mInputManagerFuncs;
+
+ // The synchronization root for the display manager.
+ // This lock guards most of the display manager's state.
+ private final SyncRoot mSyncRoot = new SyncRoot();
+
+ // True if in safe mode.
+ // This option may disable certain display adapters.
+ public boolean mSafeMode;
+
+ // True if we are in a special boot mode where only core applications and
+ // services should be started. This option may disable certain display adapters.
+ public boolean mOnlyCore;
+
+ // All callback records indexed by calling process id.
+ public final SparseArray<CallbackRecord> mCallbacks =
+ new SparseArray<CallbackRecord>();
+
+ // List of all currently registered display adapters.
+ private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
+
+ // List of all currently connected display devices.
+ private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>();
+
+ // List of all removed display devices.
+ private final ArrayList<DisplayDevice> mRemovedDisplayDevices = new ArrayList<DisplayDevice>();
+
+ // List of all logical displays indexed by logical display id.
+ private final SparseArray<LogicalDisplay> mLogicalDisplays =
+ new SparseArray<LogicalDisplay>();
+ private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
+
+ // Set to true when there are pending display changes that have yet to be applied
+ // to the surface flinger state.
+ private boolean mPendingTraversal;
+
+ // The Wifi display adapter, or null if not registered.
+ private WifiDisplayAdapter mWifiDisplayAdapter;
+
+ // Viewports of the default display and the display that should receive touch
+ // input from an external source. Used by the input system.
+ private final DisplayViewport mDefaultViewport = new DisplayViewport();
+ private final DisplayViewport mExternalTouchViewport = new DisplayViewport();
+
+ // Temporary callback list, used when sending display events to applications.
+ // May be used outside of the lock but only on the handler thread.
+ private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
+
+ // Temporary display info, used for comparing display configurations.
+ private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
+
+ // Temporary viewports, used when sending new viewport information to the
+ // input system. May be used outside of the lock but only on the handler thread.
+ private final DisplayViewport mTempDefaultViewport = new DisplayViewport();
+ private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport();
+
+ public DisplayManagerService(Context context, Handler mainHandler, Handler uiHandler) {
+ mContext = context;
+ mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
+
+ mHandler = new DisplayManagerHandler(mainHandler.getLooper());
+ mUiHandler = uiHandler;
+ mDisplayAdapterListener = new DisplayAdapterListener();
+
+ mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
+ }
+
+ /**
+ * Pauses the boot process to wait for the first display to be initialized.
+ */
+ public boolean waitForDefaultDisplay() {
+ synchronized (mSyncRoot) {
+ long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
+ while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) {
+ long delay = timeout - SystemClock.uptimeMillis();
+ if (delay <= 0) {
+ return false;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);
+ }
+ try {
+ mSyncRoot.wait(delay);
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called during initialization to associate the display manager with the
+ * window manager.
+ */
+ public void setWindowManager(WindowManagerFuncs windowManagerFuncs) {
+ synchronized (mSyncRoot) {
+ mWindowManagerFuncs = windowManagerFuncs;
+ scheduleTraversalLocked();
+ }
+ }
+
+ /**
+ * Called during initialization to associate the display manager with the
+ * input manager.
+ */
+ public void setInputManager(InputManagerFuncs inputManagerFuncs) {
+ synchronized (mSyncRoot) {
+ mInputManagerFuncs = inputManagerFuncs;
+ scheduleTraversalLocked();
+ }
+ }
+
+ /**
+ * Called when the system is ready to go.
+ */
+ public void systemReady(boolean safeMode, boolean onlyCore) {
+ synchronized (mSyncRoot) {
+ mSafeMode = safeMode;
+ mOnlyCore = onlyCore;
+ }
+
+ mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
+ }
+
+ /**
+ * Returns true if the device is headless.
+ *
+ * @return True if the device is headless.
+ */
+ public boolean isHeadless() {
+ return mHeadless;
+ }
+
+ /**
+ * Overrides the display information of a particular logical display.
+ * This is used by the window manager to control the size and characteristics
+ * of the default display. It is expected to apply the requested change
+ * to the display information synchronously so that applications will immediately
+ * observe the new state.
+ *
+ * @param displayId The logical display id.
+ * @param info The new data to be stored.
+ */
+ public void setDisplayInfoOverrideFromWindowManager(
+ int displayId, DisplayInfo info) {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+ display.setDisplayInfoOverrideFromWindowManagerLocked(info);
+ if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ scheduleTraversalLocked();
+ }
+ }
+ }
+ }
+
+ /**
+ * Called by the window manager to perform traversals while holding a
+ * surface flinger transaction.
+ */
+ public void performTraversalInTransactionFromWindowManager() {
+ synchronized (mSyncRoot) {
+ if (!mPendingTraversal) {
+ return;
+ }
+ mPendingTraversal = false;
+
+ performTraversalInTransactionLocked();
+ }
+ }
+
+ /**
+ * Returns information about the specified logical display.
+ *
+ * @param displayId The logical display id.
+ * @return The logical display info, or null if the display does not exist. The
+ * returned object must be treated as immutable.
+ */
+ @Override // Binder call
+ public DisplayInfo getDisplayInfo(int displayId) {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ return display.getDisplayInfoLocked();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Returns the list of all display ids.
+ */
+ @Override // Binder call
+ public int[] getDisplayIds() {
+ synchronized (mSyncRoot) {
+ final int count = mLogicalDisplays.size();
+ int[] displayIds = new int[count];
+ for (int i = 0; i < count; i++) {
+ displayIds[i] = mLogicalDisplays.keyAt(i);
+ }
+ return displayIds;
+ }
+ }
+
+ @Override // Binder call
+ public void registerCallback(IDisplayManagerCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mSyncRoot) {
+ int callingPid = Binder.getCallingPid();
+ if (mCallbacks.get(callingPid) != null) {
+ throw new SecurityException("The calling process has already "
+ + "registered an IDisplayManagerCallback.");
+ }
+
+ CallbackRecord record = new CallbackRecord(callingPid, callback);
+ try {
+ IBinder binder = callback.asBinder();
+ binder.linkToDeath(record, 0);
+ } catch (RemoteException ex) {
+ // give up
+ throw new RuntimeException(ex);
+ }
+
+ mCallbacks.put(callingPid, record);
+ }
+ }
+
+ private void onCallbackDied(int pid) {
+ synchronized (mSyncRoot) {
+ mCallbacks.remove(pid);
+ }
+ }
+
+ @Override // Binder call
+ public void scanWifiDisplays() {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mWifiDisplayAdapter != null) {
+ mWifiDisplayAdapter.requestScanLocked();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void connectWifiDisplay(String address) {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+ if (address == null) {
+ throw new IllegalArgumentException("address must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mWifiDisplayAdapter != null) {
+ mWifiDisplayAdapter.requestConnectLocked(address);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void disconnectWifiDisplay() {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mWifiDisplayAdapter != null) {
+ mWifiDisplayAdapter.requestDisconnectLocked();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public WifiDisplayStatus getWifiDisplayStatus() {
+ if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mWifiDisplayAdapter != null) {
+ return mWifiDisplayAdapter.getWifiDisplayStatusLocked();
+ } else {
+ return new WifiDisplayStatus();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void registerDefaultDisplayAdapter() {
+ // Register default display adapter.
+ synchronized (mSyncRoot) {
+ if (mHeadless) {
+ registerDisplayAdapterLocked(new HeadlessDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ } else {
+ registerDisplayAdapterLocked(new LocalDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ }
+ }
+ }
+
+ private void registerAdditionalDisplayAdapters() {
+ synchronized (mSyncRoot) {
+ if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
+ registerDisplayAdapterLocked(new OverlayDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
+ mWifiDisplayAdapter = new WifiDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener);
+ registerDisplayAdapterLocked(mWifiDisplayAdapter);
+ }
+ }
+ }
+
+ private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() {
+ // In safe mode, we disable non-essential display adapters to give the user
+ // an opportunity to fix broken settings or other problems that might affect
+ // system stability.
+ // In only-core mode, we disable non-essential display adapters to minimize
+ // the number of dependencies that are started while in this mode and to
+ // prevent problems that might occur due to the device being encrypted.
+ return !mSafeMode && !mOnlyCore;
+ }
+
+ private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
+ mDisplayAdapters.add(adapter);
+ adapter.registerLocked();
+ }
+
+ private void handleDisplayDeviceAdded(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ if (mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to add already added display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ mDisplayDevices.add(device);
+ addLogicalDisplayLocked(device);
+ scheduleTraversalLocked();
+ }
+ }
+
+ private void handleDisplayDeviceChanged(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ if (!mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to change non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ device.applyPendingDisplayDeviceInfoChangesLocked();
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked();
+ }
+ }
+ }
+
+ private void handleDisplayDeviceRemoved(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ if (!mDisplayDevices.remove(device)) {
+ Slog.w(TAG, "Attempted to remove non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ mRemovedDisplayDevices.add(device);
+ updateLogicalDisplaysLocked();
+ scheduleTraversalLocked();
+ }
+ }
+
+ // Adds a new logical display based on the given display device.
+ // Sends notifications if needed.
+ private void addLogicalDisplayLocked(DisplayDevice device) {
+ DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
+ boolean isDefault = (deviceInfo.flags
+ & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
+ if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
+ Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
+ isDefault = false;
+ }
+
+ final int displayId = assignDisplayIdLocked(isDefault);
+ final int layerStack = assignLayerStackLocked(displayId);
+
+ LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
+ display.updateLocked(mDisplayDevices);
+ if (!display.isValidLocked()) {
+ // This should never happen currently.
+ Slog.w(TAG, "Ignoring display device because the logical display "
+ + "created from it was not considered valid: " + deviceInfo);
+ return;
+ }
+
+ mLogicalDisplays.put(displayId, display);
+
+ // Wake up waitForDefaultDisplay.
+ if (isDefault) {
+ mSyncRoot.notifyAll();
+ }
+
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ }
+
+ private int assignDisplayIdLocked(boolean isDefault) {
+ return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
+ }
+
+ private int assignLayerStackLocked(int displayId) {
+ // Currently layer stacks and display ids are the same.
+ // This need not be the case.
+ return displayId;
+ }
+
+ // Updates all existing logical displays given the current set of display devices.
+ // Removes invalid logical displays.
+ // Sends notifications if needed.
+ private boolean updateLogicalDisplaysLocked() {
+ boolean changed = false;
+ for (int i = mLogicalDisplays.size(); i-- > 0; ) {
+ final int displayId = mLogicalDisplays.keyAt(i);
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+
+ mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+ display.updateLocked(mDisplayDevices);
+ if (!display.isValidLocked()) {
+ mLogicalDisplays.removeAt(i);
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ changed = true;
+ } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ private void performTraversalInTransactionLocked() {
+ // Perform one last traversal for each removed display device.
+ final int removedCount = mRemovedDisplayDevices.size();
+ for (int i = 0; i < removedCount; i++) {
+ DisplayDevice device = mRemovedDisplayDevices.get(i);
+ device.performTraversalInTransactionLocked();
+ }
+ mRemovedDisplayDevices.clear();
+
+ // Clear all viewports before configuring displays so that we can keep
+ // track of which ones we have configured.
+ clearViewportsLocked();
+
+ // Configure each display device.
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ DisplayDevice device = mDisplayDevices.get(i);
+ configureDisplayInTransactionLocked(device);
+ device.performTraversalInTransactionLocked();
+ }
+
+ // Tell the input system about these new viewports.
+ if (mInputManagerFuncs != null) {
+ mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
+ }
+ }
+
+ /**
+ * Tells the display manager whether there is interesting unique content on the
+ * specified logical display. This is used to control automatic mirroring.
+ * <p>
+ * If the display has unique content, then the display manager arranges for it
+ * to be presented on a physical display if appropriate. Otherwise, the display manager
+ * may choose to make the physical display mirror some other logical display.
+ * </p>
+ *
+ * @param displayId The logical display id to update.
+ * @param hasContent True if the logical display has content.
+ */
+ public void setDisplayHasContent(int displayId, boolean hasContent) {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null && display.hasContentLocked() != hasContent) {
+ display.setHasContentLocked(hasContent);
+ scheduleTraversalLocked();
+ }
+ }
+ }
+
+ private void clearViewportsLocked() {
+ mDefaultViewport.valid = false;
+ mExternalTouchViewport.valid = false;
+ }
+
+ private void configureDisplayInTransactionLocked(DisplayDevice device) {
+ // Find the logical display that the display device is showing.
+ LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
+ if (display != null && !display.hasContentLocked()) {
+ display = null;
+ }
+ if (display == null) {
+ display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ }
+
+ // Apply the logical display configuration to the display device.
+ if (display == null) {
+ // TODO: no logical display for the device, blank it
+ Slog.w(TAG, "Missing logical display to use for physical display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ } else {
+ display.configureDisplayInTransactionLocked(device);
+ }
+
+ // Update the viewports if needed.
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (!mDefaultViewport.valid
+ && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
+ setViewportLocked(mDefaultViewport, display, device);
+ }
+ if (!mExternalTouchViewport.valid
+ && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+ setViewportLocked(mExternalTouchViewport, display, device);
+ }
+ }
+
+ private static void setViewportLocked(DisplayViewport viewport,
+ LogicalDisplay display, DisplayDevice device) {
+ viewport.valid = true;
+ viewport.displayId = display.getDisplayIdLocked();
+ device.populateViewportLocked(viewport);
+ }
+
+ private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
+ final int count = mLogicalDisplays.size();
+ for (int i = 0; i < count; i++) {
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ if (display.getPrimaryDisplayDeviceLocked() == device) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ private void sendDisplayEventLocked(int displayId, int event) {
+ Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
+ mHandler.sendMessage(msg);
+ }
+
+ // Requests that performTraversalsInTransactionFromWindowManager be called at a
+ // later time to apply changes to surfaces and displays.
+ private void scheduleTraversalLocked() {
+ if (!mPendingTraversal && mWindowManagerFuncs != null) {
+ mPendingTraversal = true;
+ mHandler.sendEmptyMessage(MSG_REQUEST_TRAVERSAL);
+ }
+ }
+
+ // Runs on Handler thread.
+ // Delivers display event notifications to callbacks.
+ private void deliverDisplayEvent(int displayId, int event) {
+ if (DEBUG) {
+ Slog.d(TAG, "Delivering display event: displayId="
+ + displayId + ", event=" + event);
+ }
+
+ // Grab the lock and copy the callbacks.
+ final int count;
+ synchronized (mSyncRoot) {
+ count = mCallbacks.size();
+ mTempCallbacks.clear();
+ for (int i = 0; i < count; i++) {
+ mTempCallbacks.add(mCallbacks.valueAt(i));
+ }
+ }
+
+ // After releasing the lock, send the notifications out.
+ for (int i = 0; i < count; i++) {
+ mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
+ }
+ mTempCallbacks.clear();
+ }
+
+ @Override // Binder call
+ public void dump(FileDescriptor fd, final 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)");
+ pw.println(" mHeadless=" + mHeadless);
+
+ synchronized (mSyncRoot) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.increaseIndent();
+
+ pw.println();
+ pw.println("Display Adapters: size=" + mDisplayAdapters.size());
+ for (DisplayAdapter adapter : mDisplayAdapters) {
+ pw.println(" " + adapter.getName());
+ adapter.dumpLocked(ipw);
+ }
+
+ pw.println();
+ pw.println("Display Devices: size=" + mDisplayDevices.size());
+ for (DisplayDevice device : mDisplayDevices) {
+ pw.println(" " + device.getDisplayDeviceInfoLocked());
+ device.dumpLocked(ipw);
+ }
+
+ final int logicalDisplayCount = mLogicalDisplays.size();
+ pw.println();
+ pw.println("Logical Displays: size=" + logicalDisplayCount);
+ for (int i = 0; i < logicalDisplayCount; i++) {
+ int displayId = mLogicalDisplays.keyAt(i);
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ pw.println(" Display " + displayId + ":");
+ display.dumpLocked(ipw);
+ }
+
+ pw.println();
+ pw.println("Default viewport: " + mDefaultViewport);
+ pw.println("External touch viewport: " + mExternalTouchViewport);
+ }
+ }
+
+ /**
+ * This is the object that everything in the display manager locks on.
+ * We make it an inner class within the {@link DisplayManagerService} to so that it is
+ * clear that the object belongs to the display manager service and that it is
+ * a unique object with a special purpose.
+ */
+ public static final class SyncRoot {
+ }
+
+ /**
+ * Private interface to the window manager.
+ */
+ public interface WindowManagerFuncs {
+ /**
+ * Request that the window manager call
+ * {@link #performTraversalInTransactionFromWindowManager} within a surface
+ * transaction at a later time.
+ */
+ void requestTraversal();
+ }
+
+ /**
+ * Private interface to the input manager.
+ */
+ public interface InputManagerFuncs {
+ /**
+ * Sets information about the displays as needed by the input system.
+ * The input system should copy this information if required.
+ */
+ void setDisplayViewports(DisplayViewport defaultViewport,
+ DisplayViewport externalTouchViewport);
+ }
+
+ private final class DisplayManagerHandler extends Handler {
+ public DisplayManagerHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
+ registerDefaultDisplayAdapter();
+ break;
+
+ case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
+ registerAdditionalDisplayAdapters();
+ break;
+
+ case MSG_DELIVER_DISPLAY_EVENT:
+ deliverDisplayEvent(msg.arg1, msg.arg2);
+ break;
+
+ case MSG_REQUEST_TRAVERSAL:
+ mWindowManagerFuncs.requestTraversal();
+ break;
+
+ case MSG_UPDATE_VIEWPORT: {
+ synchronized (mSyncRoot) {
+ mTempDefaultViewport.copyFrom(mDefaultViewport);
+ mTempExternalTouchViewport.copyFrom(mExternalTouchViewport);
+ }
+ mInputManagerFuncs.setDisplayViewports(
+ mTempDefaultViewport, mTempExternalTouchViewport);
+ break;
+ }
+ }
+ }
+ }
+
+ private final class DisplayAdapterListener implements DisplayAdapter.Listener {
+ @Override
+ public void onDisplayDeviceEvent(DisplayDevice device, int event) {
+ switch (event) {
+ case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
+ handleDisplayDeviceAdded(device);
+ break;
+
+ case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
+ handleDisplayDeviceChanged(device);
+ break;
+
+ case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
+ handleDisplayDeviceRemoved(device);
+ break;
+ }
+ }
+
+ @Override
+ public void onTraversalRequested() {
+ synchronized (mSyncRoot) {
+ scheduleTraversalLocked();
+ }
+ }
+ }
+
+ private final class CallbackRecord implements DeathRecipient {
+ private final int mPid;
+ private final IDisplayManagerCallback mCallback;
+
+ public CallbackRecord(int pid, IDisplayManagerCallback callback) {
+ mPid = pid;
+ mCallback = callback;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Display listener for pid " + mPid + " died.");
+ }
+ onCallbackDied(mPid);
+ }
+
+ public void notifyDisplayEventAsync(int displayId, int event) {
+ try {
+ mCallback.onDisplayEvent(displayId, event);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process "
+ + mPid + " that displays changed, assuming it died.", ex);
+ binderDied();
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/display/DisplayViewport.java b/services/java/com/android/server/display/DisplayViewport.java
new file mode 100644
index 0000000..5080556
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayViewport.java
@@ -0,0 +1,75 @@
+/*
+ * 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.graphics.Rect;
+
+/**
+ * Describes how the pixels of physical display device reflects the content of
+ * a logical display.
+ * <p>
+ * This information is used by the input system to translate touch input from
+ * physical display coordinates into logical display coordinates.
+ * </p>
+ */
+public final class DisplayViewport {
+ // True if this viewport is valid.
+ public boolean valid;
+
+ // The logical display id.
+ public int displayId;
+
+ // The rotation applied to the physical coordinate system.
+ public int orientation;
+
+ // The portion of the logical display that are presented on this physical display.
+ public final Rect logicalFrame = new Rect();
+
+ // The portion of the (rotated) physical display that shows the logical display contents.
+ // The relation between logical and physical frame defines how the coordinate system
+ // should be scaled or translated after rotation.
+ public final Rect physicalFrame = new Rect();
+
+ // The full width and height of the display device, rotated in the same
+ // manner as physicalFrame. This expresses the full native size of the display device.
+ // The physical frame should usually fit within this area.
+ public int deviceWidth;
+ public int deviceHeight;
+
+ public void copyFrom(DisplayViewport viewport) {
+ valid = viewport.valid;
+ displayId = viewport.displayId;
+ orientation = viewport.orientation;
+ logicalFrame.set(viewport.logicalFrame);
+ physicalFrame.set(viewport.physicalFrame);
+ deviceWidth = viewport.deviceWidth;
+ deviceHeight = viewport.deviceHeight;
+ }
+
+ // For debugging purposes.
+ @Override
+ public String toString() {
+ return "DisplayViewport{valid=" + valid
+ + ", displayId=" + displayId
+ + ", orientation=" + orientation
+ + ", logicalFrame=" + logicalFrame
+ + ", physicalFrame=" + physicalFrame
+ + ", deviceWidth=" + deviceWidth
+ + ", deviceHeight=" + deviceHeight
+ + "}";
+ }
+}
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..f3bec1d
--- /dev/null
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -0,0 +1,69 @@
+/*
+ * 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.os.Handler;
+import android.util.DisplayMetrics;
+
+/**
+ * Provides a fake default display for headless systems.
+ * <p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class HeadlessDisplayAdapter extends DisplayAdapter {
+ private static final String TAG = "HeadlessDisplayAdapter";
+
+ public HeadlessDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
+ }
+
+ @Override
+ public void registerLocked() {
+ super.registerLocked();
+ sendDisplayDeviceEventLocked(new HeadlessDisplayDevice(), DISPLAY_DEVICE_EVENT_ADDED);
+ }
+
+ private final class HeadlessDisplayDevice extends DisplayDevice {
+ private DisplayDeviceInfo mInfo;
+
+ public HeadlessDisplayDevice() {
+ super(HeadlessDisplayAdapter.this, null);
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
+ mInfo.width = 640;
+ mInfo.height = 480;
+ mInfo.refreshRate = 60;
+ mInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+ mInfo.xDpi = 160;
+ mInfo.yDpi = 160;
+ mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+ }
+ return mInfo;
+ }
+ }
+}
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
new file mode 100644
index 0000000..eab4c9a
--- /dev/null
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -0,0 +1,151 @@
+/*
+ * 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.os.Handler;
+import android.os.IBinder;
+import android.util.SparseArray;
+import android.view.Surface;
+import android.view.Surface.PhysicalDisplayInfo;
+
+import java.io.PrintWriter;
+
+/**
+ * A display adapter for the local displays managed by Surface Flinger.
+ * <p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class LocalDisplayAdapter extends DisplayAdapter {
+ private static final String TAG = "LocalDisplayAdapter";
+
+ private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
+ Surface.BUILT_IN_DISPLAY_ID_MAIN,
+ Surface.BUILT_IN_DISPLAY_ID_HDMI,
+ };
+
+ private final SparseArray<LocalDisplayDevice> mDevices =
+ new SparseArray<LocalDisplayDevice>();
+
+ private final PhysicalDisplayInfo mTempPhys = new PhysicalDisplayInfo();
+
+ public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
+ }
+
+ @Override
+ public void registerLocked() {
+ // TODO: listen for notifications from Surface Flinger about
+ // built-in displays being added or removed and rescan as needed.
+ super.registerLocked();
+ scanDisplaysLocked();
+ }
+
+ private void scanDisplaysLocked() {
+ for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
+ IBinder displayToken = Surface.getBuiltInDisplay(builtInDisplayId);
+ if (displayToken != null && Surface.getDisplayInfo(displayToken, mTempPhys)) {
+ LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ if (device == null) {
+ // Display was added.
+ device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys);
+ mDevices.put(builtInDisplayId, device);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
+ } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) {
+ // Display properties changed.
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
+ } else {
+ LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ if (device != null) {
+ // Display was removed.
+ mDevices.remove(builtInDisplayId);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+ }
+ }
+
+ private final class LocalDisplayDevice extends DisplayDevice {
+ private final int mBuiltInDisplayId;
+ private final PhysicalDisplayInfo mPhys;
+
+ private DisplayDeviceInfo mInfo;
+ private boolean mHavePendingChanges;
+
+ public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
+ PhysicalDisplayInfo phys) {
+ super(LocalDisplayAdapter.this, displayToken);
+ mBuiltInDisplayId = builtInDisplayId;
+ mPhys = new PhysicalDisplayInfo(phys);
+ }
+
+ public boolean updatePhysicalDisplayInfoLocked(PhysicalDisplayInfo phys) {
+ if (!mPhys.equals(phys)) {
+ mPhys.copyFrom(phys);
+ mHavePendingChanges = true;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void applyPendingDisplayDeviceInfoChangesLocked() {
+ if (mHavePendingChanges) {
+ mInfo = null;
+ mHavePendingChanges = false;
+ }
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.width = mPhys.width;
+ mInfo.height = mPhys.height;
+ mInfo.refreshRate = mPhys.refreshRate;
+ if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
+ mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT
+ | DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION;
+ mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
+ mInfo.xDpi = mPhys.xDpi;
+ mInfo.yDpi = mPhys.yDpi;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
+ } else {
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_hdmi_display_name);
+ mInfo.flags = DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
+ mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
+ }
+ }
+ return mInfo;
+ }
+
+ @Override
+ public void dumpLocked(PrintWriter pw) {
+ super.dumpLocked(pw);
+ pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
+ pw.println("mPhys=" + mPhys);
+ }
+ }
+}
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
new file mode 100644
index 0000000..3607de1
--- /dev/null
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -0,0 +1,308 @@
+/*
+ * 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.graphics.Rect;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import libcore.util.Objects;
+
+/**
+ * Describes how a logical display is configured.
+ * <p>
+ * At this time, we only support logical displays that are coupled to a particular
+ * primary display device from which the logical display derives its basic properties
+ * such as its size, density and refresh rate.
+ * </p><p>
+ * A logical display may be mirrored onto multiple display devices in addition to its
+ * primary display device. Note that the contents of a logical display may not
+ * always be visible, even on its primary display device, such as in the case where
+ * the primary display device is currently mirroring content from a different
+ * logical display.
+ * </p><p>
+ * This object is designed to encapsulate as much of the policy of logical
+ * displays as possible. The idea is to make it easy to implement new kinds of
+ * logical displays mostly by making local changes to this class.
+ * </p><p>
+ * Note: The display manager architecture does not actually require logical displays
+ * to be associated with any individual display device. Logical displays and
+ * display devices are orthogonal concepts. Some mapping will exist between
+ * logical displays and display devices but it can be many-to-many and
+ * and some might have no relation at all.
+ * </p><p>
+ * Logical displays are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class LogicalDisplay {
+ private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
+
+ private final int mDisplayId;
+ private final int mLayerStack;
+ private DisplayInfo mOverrideDisplayInfo; // set by the window manager
+ private DisplayInfo mInfo;
+
+ // The display device that this logical display is based on and which
+ // determines the base metrics that it uses.
+ private DisplayDevice mPrimaryDisplayDevice;
+ private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;
+
+ // True if the logical display has unique content.
+ private boolean mHasContent;
+
+ // Temporary rectangle used when needed.
+ private final Rect mTempLayerStackRect = new Rect();
+ private final Rect mTempDisplayRect = new Rect();
+
+ public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
+ mDisplayId = displayId;
+ mLayerStack = layerStack;
+ mPrimaryDisplayDevice = primaryDisplayDevice;
+ }
+
+ /**
+ * Gets the logical display id of this logical display.
+ *
+ * @return The logical display id.
+ */
+ public int getDisplayIdLocked() {
+ return mDisplayId;
+ }
+
+ /**
+ * Gets the primary display device associated with this logical display.
+ *
+ * @return The primary display device.
+ */
+ public DisplayDevice getPrimaryDisplayDeviceLocked() {
+ return mPrimaryDisplayDevice;
+ }
+
+ /**
+ * Gets information about the logical display.
+ *
+ * @return The device info, which should be treated as immutable by the caller.
+ * The logical display should allocate a new display info object whenever
+ * the data changes.
+ */
+ public DisplayInfo getDisplayInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayInfo();
+ if (mOverrideDisplayInfo != null) {
+ mInfo.copyFrom(mOverrideDisplayInfo);
+ mInfo.layerStack = mBaseDisplayInfo.layerStack;
+ mInfo.name = mBaseDisplayInfo.name;
+ } else {
+ mInfo.copyFrom(mBaseDisplayInfo);
+ }
+ }
+ return mInfo;
+ }
+
+ /**
+ * Sets overridden logical display information from the window manager.
+ * This method can be used to adjust application insets, rotation, and other
+ * properties that the window manager takes care of.
+ *
+ * @param info The logical display information, may be null.
+ */
+ public void setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
+ if (info != null) {
+ if (mOverrideDisplayInfo == null) {
+ mOverrideDisplayInfo = new DisplayInfo(info);
+ mInfo = null;
+ } else if (!mOverrideDisplayInfo.equals(info)) {
+ mOverrideDisplayInfo.copyFrom(info);
+ mInfo = null;
+ }
+ } else if (mOverrideDisplayInfo != null) {
+ mOverrideDisplayInfo = null;
+ mInfo = null;
+ }
+ }
+
+ /**
+ * Returns true if the logical display is in a valid state.
+ * This method should be checked after calling {@link #updateLocked} to handle the
+ * case where a logical display should be removed because all of its associated
+ * display devices are gone or if it is otherwise no longer needed.
+ *
+ * @return True if the logical display is still valid.
+ */
+ public boolean isValidLocked() {
+ return mPrimaryDisplayDevice != null;
+ }
+
+ /**
+ * Updates the state of the logical display based on the available display devices.
+ * The logical display might become invalid if it is attached to a display device
+ * that no longer exists.
+ *
+ * @param devices The list of all connected display devices.
+ */
+ public void updateLocked(List<DisplayDevice> devices) {
+ // Nothing to update if already invalid.
+ if (mPrimaryDisplayDevice == null) {
+ return;
+ }
+
+ // Check whether logical display has become invalid.
+ if (!devices.contains(mPrimaryDisplayDevice)) {
+ mPrimaryDisplayDevice = null;
+ return;
+ }
+
+ // Bootstrap the logical display using its associated primary physical display.
+ // We might use more elaborate configurations later. It's possible that the
+ // configuration of several physical displays might be used to determine the
+ // logical display that they are sharing. (eg. Adjust size for pixel-perfect
+ // mirroring over HDMI.)
+ DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked();
+ if (!Objects.equal(mPrimaryDisplayDeviceInfo, deviceInfo)) {
+ mBaseDisplayInfo.layerStack = mLayerStack;
+ mBaseDisplayInfo.flags = 0;
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ }
+ mBaseDisplayInfo.name = deviceInfo.name;
+ mBaseDisplayInfo.appWidth = deviceInfo.width;
+ mBaseDisplayInfo.appHeight = deviceInfo.height;
+ mBaseDisplayInfo.logicalWidth = deviceInfo.width;
+ mBaseDisplayInfo.logicalHeight = deviceInfo.height;
+ mBaseDisplayInfo.rotation = Surface.ROTATION_0;
+ mBaseDisplayInfo.refreshRate = deviceInfo.refreshRate;
+ mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
+ mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
+ mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
+ mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
+ mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
+ mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
+ mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
+
+ mPrimaryDisplayDeviceInfo = deviceInfo;
+ mInfo = null;
+ }
+ }
+
+ /**
+ * Applies the layer stack and transformation to the given display device
+ * so that it shows the contents of this logical display.
+ *
+ * We know that the given display device is only ever showing the contents of
+ * a single logical display, so this method is expected to blow away all of its
+ * transformation properties to make it happen regardless of what the
+ * display device was previously showing.
+ *
+ * The caller must have an open Surface transaction.
+ *
+ * The display device may not be the primary display device, in the case
+ * where the display is being mirrored.
+ *
+ * @param device The display device to modify.
+ */
+ public void configureDisplayInTransactionLocked(DisplayDevice device) {
+ final DisplayInfo displayInfo = getDisplayInfoLocked();
+ final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
+
+ // Set the layer stack.
+ device.setLayerStackInTransactionLocked(mLayerStack);
+
+ // Set the viewport.
+ // This is the area of the logical display that we intend to show on the
+ // display device. For now, it is always the full size of the logical display.
+ mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ // Set the orientation.
+ // The orientation specifies how the physical coordinate system of the display
+ // is rotated when the contents of the logical display are rendered.
+ int orientation = Surface.ROTATION_0;
+ if (device == mPrimaryDisplayDevice
+ && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) {
+ orientation = displayInfo.rotation;
+ }
+
+ // Set the frame.
+ // The frame specifies the rotated physical coordinates into which the viewport
+ // is mapped. We need to take care to preserve the aspect ratio of the viewport.
+ // Currently we maximize the area to fill the display, but we could try to be
+ // more clever and match resolutions.
+ boolean rotated = (orientation == Surface.ROTATION_90
+ || orientation == Surface.ROTATION_270);
+ int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
+ int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
+
+ // Determine whether the width or height is more constrained to be scaled.
+ // physWidth / displayInfo.logicalWidth => letter box
+ // or physHeight / displayInfo.logicalHeight => pillar box
+ //
+ // We avoid a division (and possible floating point imprecision) here by
+ // multiplying the fractions by the product of their denominators before
+ // comparing them.
+ int displayRectWidth, displayRectHeight;
+ if (physWidth * displayInfo.logicalHeight
+ < physHeight * displayInfo.logicalWidth) {
+ // Letter box.
+ displayRectWidth = physWidth;
+ displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
+ } else {
+ // Pillar box.
+ displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
+ displayRectHeight = physHeight;
+ }
+ int displayRectTop = (physHeight - displayRectHeight) / 2;
+ int displayRectLeft = (physWidth - displayRectWidth) / 2;
+ mTempDisplayRect.set(displayRectLeft, displayRectTop,
+ displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
+
+ device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
+ }
+
+ /**
+ * Returns true if the logical display has unique content.
+ * <p>
+ * If the display has unique content then we will try to ensure that it is
+ * visible on at least its primary display device. Otherwise we will ignore the
+ * logical display and perhaps show mirrored content on the primary display device.
+ * </p>
+ *
+ * @return True if the display has unique content.
+ */
+ public boolean hasContentLocked() {
+ return mHasContent;
+ }
+
+ /**
+ * Sets whether the logical display has unique content.
+ *
+ * @param hasContent True if the display has unique content.
+ */
+ public void setHasContentLocked(boolean hasContent) {
+ mHasContent = hasContent;
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println("mLayerStack=" + mLayerStack);
+ pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
+ mPrimaryDisplayDevice.getNameLocked() : "null"));
+ pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
+ pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
new file mode 100644
index 0000000..75ddd24
--- /dev/null
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -0,0 +1,338 @@
+/*
+ * 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 com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A display adapter that uses overlay windows to simulate secondary displays
+ * for development purposes. Use Development Settings to enable one or more
+ * overlay displays.
+ * <p>
+ * This object has two different handlers (which may be the same) which must not
+ * get confused. The main handler is used to posting messages to the display manager
+ * service as usual. The UI handler is only used by the {@link OverlayDisplayWindow}.
+ * </p><p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class OverlayDisplayAdapter extends DisplayAdapter {
+ static final String TAG = "OverlayDisplayAdapter";
+ static final boolean DEBUG = false;
+
+ private static final int MIN_WIDTH = 100;
+ private static final int MIN_HEIGHT = 100;
+ private static final int MAX_WIDTH = 4096;
+ private static final int MAX_HEIGHT = 4096;
+
+ private static final Pattern SETTING_PATTERN =
+ Pattern.compile("(\\d+)x(\\d+)/(\\d+)");
+
+ private final Handler mUiHandler;
+ private final ArrayList<OverlayDisplayHandle> mOverlays =
+ new ArrayList<OverlayDisplayHandle>();
+ private String mCurrentOverlaySetting = "";
+
+ public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener, Handler uiHandler) {
+ super(syncRoot, context, handler, listener, TAG);
+ mUiHandler = uiHandler;
+ }
+
+ @Override
+ public void dumpLocked(PrintWriter pw) {
+ super.dumpLocked(pw);
+
+ pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
+ pw.println("mOverlays: size=" + mOverlays.size());
+ for (OverlayDisplayHandle overlay : mOverlays) {
+ overlay.dumpLocked(pw);
+ }
+ }
+
+ @Override
+ public void registerLocked() {
+ super.registerLocked();
+
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ getContext().getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES),
+ true, new SettingsObserver(getHandler()));
+
+ synchronized (getSyncRoot()) {
+ updateOverlayDisplayDevicesLocked();
+ }
+ }
+ });
+ }
+
+ private void updateOverlayDisplayDevicesLocked() {
+ String value = Settings.System.getString(getContext().getContentResolver(),
+ Settings.Secure.OVERLAY_DISPLAY_DEVICES);
+ if (value == null) {
+ value = "";
+ }
+
+ if (value.equals(mCurrentOverlaySetting)) {
+ return;
+ }
+ mCurrentOverlaySetting = value;
+
+ if (!mOverlays.isEmpty()) {
+ Slog.i(TAG, "Dismissing all overlay display devices.");
+ for (OverlayDisplayHandle overlay : mOverlays) {
+ overlay.dismissLocked();
+ }
+ mOverlays.clear();
+ }
+
+ int count = 0;
+ for (String part : value.split(";")) {
+ Matcher matcher = SETTING_PATTERN.matcher(part);
+ if (matcher.matches()) {
+ if (count >= 4) {
+ Slog.w(TAG, "Too many overlay display devices specified: " + value);
+ break;
+ }
+ try {
+ int width = Integer.parseInt(matcher.group(1), 10);
+ int height = Integer.parseInt(matcher.group(2), 10);
+ int densityDpi = Integer.parseInt(matcher.group(3), 10);
+ if (width >= MIN_WIDTH && width <= MAX_WIDTH
+ && height >= MIN_HEIGHT && height <= MAX_HEIGHT
+ && densityDpi >= DisplayMetrics.DENSITY_LOW
+ && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
+ int number = ++count;
+ String name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_name,
+ number);
+ int gravity = chooseOverlayGravity(number);
+
+ Slog.i(TAG, "Showing overlay display device #" + number
+ + ": name=" + name + ", width=" + width + ", height=" + height
+ + ", densityDpi=" + densityDpi);
+
+ mOverlays.add(new OverlayDisplayHandle(name,
+ width, height, densityDpi, gravity));
+ continue;
+ }
+ } catch (NumberFormatException ex) {
+ }
+ } else if (part.isEmpty()) {
+ continue;
+ }
+ Slog.w(TAG, "Malformed overlay display devices setting: " + value);
+ }
+ }
+
+ private static int chooseOverlayGravity(int overlayNumber) {
+ switch (overlayNumber) {
+ case 1:
+ return Gravity.TOP | Gravity.LEFT;
+ case 2:
+ return Gravity.BOTTOM | Gravity.RIGHT;
+ case 3:
+ return Gravity.TOP | Gravity.RIGHT;
+ case 4:
+ default:
+ return Gravity.BOTTOM | Gravity.LEFT;
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (getSyncRoot()) {
+ updateOverlayDisplayDevicesLocked();
+ }
+ }
+ }
+
+ private final class OverlayDisplayDevice extends DisplayDevice {
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final float mRefreshRate;
+ private final int mDensityDpi;
+
+ private Surface mSurface;
+ private DisplayDeviceInfo mInfo;
+
+ public OverlayDisplayDevice(IBinder displayToken, String name,
+ int width, int height, float refreshRate, int densityDpi,
+ Surface surface) {
+ super(OverlayDisplayAdapter.this, displayToken);
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mRefreshRate = refreshRate;
+ mDensityDpi = densityDpi;
+ mSurface = surface;
+ }
+
+ public void clearSurfaceLocked() {
+ mSurface = null;
+ sendTraversalRequestLocked();
+ }
+
+ @Override
+ public void performTraversalInTransactionLocked() {
+ setSurfaceInTransactionLocked(mSurface);
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = mName;
+ mInfo.width = mWidth;
+ mInfo.height = mHeight;
+ mInfo.refreshRate = mRefreshRate;
+ mInfo.densityDpi = mDensityDpi;
+ mInfo.xDpi = mDensityDpi;
+ mInfo.yDpi = mDensityDpi;
+ mInfo.flags = DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+ }
+ return mInfo;
+ }
+ }
+
+ /**
+ * Functions as a handle for overlay display devices which are created and
+ * destroyed asynchronously.
+ *
+ * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ */
+ private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private final int mGravity;
+
+ private OverlayDisplayWindow mWindow;
+ private OverlayDisplayDevice mDevice;
+
+ public OverlayDisplayHandle(String name,
+ int width, int height, int densityDpi, int gravity) {
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mGravity = gravity;
+
+ mUiHandler.post(mShowRunnable);
+ }
+
+ public void dismissLocked() {
+ mUiHandler.removeCallbacks(mShowRunnable);
+ mUiHandler.post(mDismissRunnable);
+ }
+
+ // Called on the UI thread.
+ @Override
+ public void onWindowCreated(Surface surface, float refreshRate) {
+ synchronized (getSyncRoot()) {
+ IBinder displayToken = Surface.createDisplay(mName);
+ mDevice = new OverlayDisplayDevice(displayToken, mName,
+ mWidth, mHeight, refreshRate, mDensityDpi, surface);
+
+ sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
+ }
+ }
+
+ // Called on the UI thread.
+ @Override
+ public void onWindowDestroyed() {
+ synchronized (getSyncRoot()) {
+ if (mDevice != null) {
+ mDevice.clearSurfaceLocked();
+ sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println(" " + mName + ":");
+ pw.println(" mWidth=" + mWidth);
+ pw.println(" mHeight=" + mHeight);
+ pw.println(" mDensityDpi=" + mDensityDpi);
+ pw.println(" mGravity=" + mGravity);
+
+ // Try to dump the window state.
+ if (mWindow != null) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.increaseIndent();
+ DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
+ }
+ }
+
+ // Runs on the UI thread.
+ private final Runnable mShowRunnable = new Runnable() {
+ @Override
+ public void run() {
+ OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
+ mName, mWidth, mHeight, mDensityDpi, mGravity,
+ OverlayDisplayHandle.this);
+ window.show();
+
+ synchronized (getSyncRoot()) {
+ mWindow = window;
+ }
+ }
+ };
+
+ // Runs on the UI thread.
+ private final Runnable mDismissRunnable = new Runnable() {
+ @Override
+ public void run() {
+ OverlayDisplayWindow window;
+ synchronized (getSyncRoot()) {
+ window = mWindow;
+ mWindow = null;
+ }
+
+ if (window != null) {
+ window.dismiss();
+ }
+ }
+ };
+ }
+}
diff --git a/services/java/com/android/server/display/OverlayDisplayWindow.java b/services/java/com/android/server/display/OverlayDisplayWindow.java
new file mode 100644
index 0000000..d08f65f
--- /dev/null
+++ b/services/java/com/android/server/display/OverlayDisplayWindow.java
@@ -0,0 +1,367 @@
+/*
+ * 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 com.android.internal.util.DumpUtils;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.display.DisplayManager;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.TextureView.SurfaceTextureListener;
+import android.widget.TextView;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages an overlay window on behalf of {@link OverlayDisplayAdapter}.
+ * <p>
+ * This object must only be accessed on the UI thread.
+ * No locks are held by this object and locks must not be held while making called into it.
+ * </p>
+ */
+final class OverlayDisplayWindow implements DumpUtils.Dump {
+ private static final String TAG = "OverlayDisplayWindow";
+ private static final boolean DEBUG = false;
+
+ private final float INITIAL_SCALE = 0.5f;
+ private final float MIN_SCALE = 0.3f;
+ private final float MAX_SCALE = 1.0f;
+ private final float WINDOW_ALPHA = 0.8f;
+
+ // When true, disables support for moving and resizing the overlay.
+ // The window is made non-touchable, which makes it possible to
+ // directly interact with the content underneath.
+ private final boolean DISABLE_MOVE_AND_RESIZE = false;
+
+ private final Context mContext;
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private final int mGravity;
+ private final Listener mListener;
+ private final String mTitle;
+
+ private final DisplayManager mDisplayManager;
+ private final WindowManager mWindowManager;
+
+
+ private final Display mDefaultDisplay;
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+
+ private View mWindowContent;
+ private WindowManager.LayoutParams mWindowParams;
+ private TextureView mTextureView;
+ private TextView mTitleTextView;
+
+ private GestureDetector mGestureDetector;
+ private ScaleGestureDetector mScaleGestureDetector;
+
+ private boolean mWindowVisible;
+ private int mWindowX;
+ private int mWindowY;
+ private float mWindowScale;
+
+ private float mLiveTranslationX;
+ private float mLiveTranslationY;
+ private float mLiveScale = 1.0f;
+
+ public OverlayDisplayWindow(Context context, String name,
+ int width, int height, int densityDpi, int gravity, Listener listener) {
+ mContext = context;
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mGravity = gravity;
+ mListener = listener;
+ mTitle = context.getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_title,
+ mName, mWidth, mHeight, mDensityDpi);
+
+ mDisplayManager = (DisplayManager)context.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mWindowManager = (WindowManager)context.getSystemService(
+ Context.WINDOW_SERVICE);
+
+ mDefaultDisplay = mWindowManager.getDefaultDisplay();
+ updateDefaultDisplayInfo();
+
+ createWindow();
+ }
+
+ public void show() {
+ if (!mWindowVisible) {
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ if (!updateDefaultDisplayInfo()) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ return;
+ }
+
+ clearLiveState();
+ updateWindowParams();
+ mWindowManager.addView(mWindowContent, mWindowParams);
+ mWindowVisible = true;
+ }
+ }
+
+ public void dismiss() {
+ if (mWindowVisible) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ mWindowManager.removeView(mWindowContent);
+ mWindowVisible = false;
+ }
+ }
+
+ public void relayout() {
+ if (mWindowVisible) {
+ updateWindowParams();
+ mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("mWindowVisible=" + mWindowVisible);
+ pw.println("mWindowX=" + mWindowX);
+ pw.println("mWindowY=" + mWindowY);
+ pw.println("mWindowScale=" + mWindowScale);
+ pw.println("mWindowParams=" + mWindowParams);
+ if (mTextureView != null) {
+ pw.println("mTextureView.getScaleX()=" + mTextureView.getScaleX());
+ pw.println("mTextureView.getScaleY()=" + mTextureView.getScaleY());
+ }
+ pw.println("mLiveTranslationX=" + mLiveTranslationX);
+ pw.println("mLiveTranslationY=" + mLiveTranslationY);
+ pw.println("mLiveScale=" + mLiveScale);
+ }
+
+ private boolean updateDefaultDisplayInfo() {
+ if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) {
+ Slog.w(TAG, "Cannot show overlay display because there is no "
+ + "default display upon which to show it.");
+ return false;
+ }
+ return true;
+ }
+
+ private void createWindow() {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ mWindowContent = inflater.inflate(
+ com.android.internal.R.layout.overlay_display_window, null);
+ mWindowContent.setOnTouchListener(mOnTouchListener);
+
+ mTextureView = (TextureView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_texture);
+ mTextureView.setPivotX(0);
+ mTextureView.setPivotY(0);
+ mTextureView.getLayoutParams().width = mWidth;
+ mTextureView.getLayoutParams().height = mHeight;
+ mTextureView.setOpaque(false);
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+
+ mTitleTextView = (TextView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_title);
+ mTitleTextView.setText(mTitle);
+
+ mWindowParams = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY);
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ if (DISABLE_MOVE_AND_RESIZE) {
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ }
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
+ mWindowParams.alpha = WINDOW_ALPHA;
+ mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
+ mWindowParams.setTitle(mTitle);
+
+ mGestureDetector = new GestureDetector(mContext, mOnGestureListener);
+ mScaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
+
+ // Set the initial position and scale.
+ // The position and scale will be clamped when the display is first shown.
+ mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT ?
+ 0 : mDefaultDisplayInfo.logicalWidth;
+ mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP ?
+ 0 : mDefaultDisplayInfo.logicalHeight;
+ mWindowScale = INITIAL_SCALE;
+ }
+
+ private void updateWindowParams() {
+ float scale = mWindowScale * mLiveScale;
+ scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalWidth / mWidth);
+ scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalHeight / mHeight);
+ scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
+
+ float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
+ int width = (int)(mWidth * scale);
+ int height = (int)(mHeight * scale);
+ int x = (int)(mWindowX + mLiveTranslationX - width * offsetScale);
+ int y = (int)(mWindowY + mLiveTranslationY - height * offsetScale);
+ x = Math.max(0, Math.min(x, mDefaultDisplayInfo.logicalWidth - width));
+ y = Math.max(0, Math.min(y, mDefaultDisplayInfo.logicalHeight - height));
+
+ if (DEBUG) {
+ Slog.d(TAG, "updateWindowParams: scale=" + scale
+ + ", offsetScale=" + offsetScale
+ + ", x=" + x + ", y=" + y
+ + ", width=" + width + ", height=" + height);
+ }
+
+ mTextureView.setScaleX(scale);
+ mTextureView.setScaleY(scale);
+
+ mWindowParams.x = x;
+ mWindowParams.y = y;
+ mWindowParams.width = width;
+ mWindowParams.height = height;
+ }
+
+ private void saveWindowParams() {
+ mWindowX = mWindowParams.x;
+ mWindowY = mWindowParams.y;
+ mWindowScale = mTextureView.getScaleX();
+ clearLiveState();
+ }
+
+ private void clearLiveState() {
+ mLiveTranslationX = 0f;
+ mLiveTranslationY = 0f;
+ mLiveScale = 1.0f;
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ if (updateDefaultDisplayInfo()) {
+ relayout();
+ } else {
+ dismiss();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ dismiss();
+ }
+ }
+ };
+
+ private final SurfaceTextureListener mSurfaceTextureListener =
+ new SurfaceTextureListener() {
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
+ int width, int height) {
+ mListener.onWindowCreated(new Surface(surfaceTexture),
+ mDefaultDisplayInfo.refreshRate);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ mListener.onWindowDestroyed();
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
+ int width, int height) {
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+ }
+ };
+
+ private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ // Work in screen coordinates.
+ final float oldX = event.getX();
+ final float oldY = event.getY();
+ event.setLocation(event.getRawX(), event.getRawY());
+
+ mGestureDetector.onTouchEvent(event);
+ mScaleGestureDetector.onTouchEvent(event);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ saveWindowParams();
+ break;
+ }
+
+ // Revert to window coordinates.
+ event.setLocation(oldX, oldY);
+ return true;
+ }
+ };
+
+ private final GestureDetector.OnGestureListener mOnGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ mLiveTranslationX -= distanceX;
+ mLiveTranslationY -= distanceY;
+ relayout();
+ return true;
+ }
+ };
+
+ private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
+ new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mLiveScale *= detector.getScaleFactor();
+ relayout();
+ return true;
+ }
+ };
+
+ /**
+ * Watches for significant changes in the overlay display window lifecycle.
+ */
+ public interface Listener {
+ public void onWindowCreated(Surface surface, float refreshRate);
+ public void onWindowDestroyed();
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
new file mode 100644
index 0000000..b57d3dc
--- /dev/null
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -0,0 +1,346 @@
+/*
+ * 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 com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.WifiDisplay;
+import android.hardware.display.WifiDisplayStatus;
+import android.media.RemoteDisplay;
+import android.os.Handler;
+import android.os.IBinder;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Connects to Wifi displays that implement the Miracast protocol.
+ * <p>
+ * The Wifi display protocol relies on Wifi direct for discovering and pairing
+ * with the display. Once connected, the Media Server opens an RTSP socket and accepts
+ * a connection from the display. After session negotiation, the Media Server
+ * streams encoded buffers to the display.
+ * </p><p>
+ * This class is responsible for connecting to Wifi displays and mediating
+ * the interactions between Media Server, Surface Flinger and the Display Manager Service.
+ * </p><p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class WifiDisplayAdapter extends DisplayAdapter {
+ private static final String TAG = "WifiDisplayAdapter";
+
+ private WifiDisplayController mDisplayController;
+ private WifiDisplayDevice mDisplayDevice;
+
+ private WifiDisplayStatus mCurrentStatus;
+ private boolean mEnabled;
+ private int mScanState;
+ private int mActiveDisplayState;
+ private WifiDisplay mActiveDisplay;
+ private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY;
+
+ private boolean mPendingStatusChangeBroadcast;
+
+ public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
+ }
+
+ @Override
+ public void dumpLocked(PrintWriter pw) {
+ super.dumpLocked(pw);
+
+ pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
+ pw.println("mEnabled=" + mEnabled);
+ pw.println("mScanState=" + mScanState);
+ pw.println("mActiveDisplayState=" + mActiveDisplayState);
+ pw.println("mActiveDisplay=" + mActiveDisplay);
+ pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays));
+ pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
+
+ // Try to dump the controller state.
+ if (mDisplayController == null) {
+ pw.println("mDisplayController=null");
+ } else {
+ pw.println("mDisplayController:");
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.increaseIndent();
+ DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200);
+ }
+ }
+
+ @Override
+ public void registerLocked() {
+ super.registerLocked();
+
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ mDisplayController = new WifiDisplayController(
+ getContext(), getHandler(), mWifiDisplayListener);
+ }
+ });
+ }
+
+ public void requestScanLocked() {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestScan();
+ }
+ }
+ });
+ }
+
+ public void requestConnectLocked(final String address) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestConnect(address);
+ }
+ }
+ });
+ }
+
+ public void requestDisconnectLocked() {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestDisconnect();
+ }
+ }
+ });
+ }
+
+ public WifiDisplayStatus getWifiDisplayStatusLocked() {
+ if (mCurrentStatus == null) {
+ mCurrentStatus = new WifiDisplayStatus(mEnabled, mScanState, mActiveDisplayState,
+ mActiveDisplay, mKnownDisplays);
+ }
+ return mCurrentStatus;
+ }
+
+ private void handleConnectLocked(WifiDisplay display,
+ Surface surface, int width, int height, int flags) {
+ handleDisconnectLocked();
+
+ int deviceFlags = 0;
+ if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
+ deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
+ }
+
+ float refreshRate = 60.0f; // TODO: get this for real
+
+ String name = display.getDeviceName();
+ IBinder displayToken = Surface.createDisplay(name);
+ mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
+ refreshRate, deviceFlags, surface);
+ sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
+ }
+
+ private void handleDisconnectLocked() {
+ if (mDisplayDevice != null) {
+ mDisplayDevice.clearSurfaceLocked();
+ sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
+ mDisplayDevice = null;
+ }
+ }
+
+ private void scheduleStatusChangedBroadcastLocked() {
+ if (!mPendingStatusChangeBroadcast) {
+ mPendingStatusChangeBroadcast = true;
+ getHandler().post(mStatusChangeBroadcast);
+ }
+ }
+
+ private final Runnable mStatusChangeBroadcast = new Runnable() {
+ @Override
+ public void run() {
+ final Intent intent;
+ synchronized (getSyncRoot()) {
+ if (!mPendingStatusChangeBroadcast) {
+ return;
+ }
+
+ mPendingStatusChangeBroadcast = false;
+ intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
+ getWifiDisplayStatusLocked());
+ }
+
+ // Send protected broadcast about wifi display status to receivers that
+ // have the required permission.
+ getContext().sendBroadcast(intent,
+ android.Manifest.permission.CONFIGURE_WIFI_DISPLAY);
+ }
+ };
+
+ private final WifiDisplayController.Listener mWifiDisplayListener =
+ new WifiDisplayController.Listener() {
+ @Override
+ public void onEnablementChanged(boolean enabled) {
+ synchronized (getSyncRoot()) {
+ if (mEnabled != enabled) {
+ mCurrentStatus = null;
+ mEnabled = enabled;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onScanStarted() {
+ synchronized (getSyncRoot()) {
+ if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) {
+ mCurrentStatus = null;
+ mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ public void onScanFinished(WifiDisplay[] knownDisplays) {
+ synchronized (getSyncRoot()) {
+ if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING
+ || !Arrays.equals(mKnownDisplays, knownDisplays)) {
+ mCurrentStatus = null;
+ mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
+ mKnownDisplays = knownDisplays;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayConnecting(WifiDisplay display) {
+ synchronized (getSyncRoot()) {
+ if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING
+ || mActiveDisplay == null
+ || !mActiveDisplay.equals(display)) {
+ mCurrentStatus = null;
+ mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING;
+ mActiveDisplay = display;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayConnectionFailed() {
+ synchronized (getSyncRoot()) {
+ if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
+ || mActiveDisplay != null) {
+ mCurrentStatus = null;
+ mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
+ mActiveDisplay = null;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayConnected(WifiDisplay display, Surface surface,
+ int width, int height, int flags) {
+ synchronized (getSyncRoot()) {
+ handleConnectLocked(display, surface, width, height, flags);
+
+ if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
+ || mActiveDisplay == null
+ || !mActiveDisplay.equals(display)) {
+ mCurrentStatus = null;
+ mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED;
+ mActiveDisplay = display;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayDisconnected() {
+ // Stop listening.
+ synchronized (getSyncRoot()) {
+ handleDisconnectLocked();
+
+ if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
+ || mActiveDisplay != null) {
+ mCurrentStatus = null;
+ mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED;
+ mActiveDisplay = null;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+ }
+ };
+
+ private final class WifiDisplayDevice extends DisplayDevice {
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final float mRefreshRate;
+ private final int mFlags;
+
+ private Surface mSurface;
+ private DisplayDeviceInfo mInfo;
+
+ public WifiDisplayDevice(IBinder displayToken, String name,
+ int width, int height, float refreshRate, int flags,
+ Surface surface) {
+ super(WifiDisplayAdapter.this, displayToken);
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mRefreshRate = refreshRate;
+ mFlags = flags;
+ mSurface = surface;
+ }
+
+ public void clearSurfaceLocked() {
+ mSurface = null;
+ sendTraversalRequestLocked();
+ }
+
+ @Override
+ public void performTraversalInTransactionLocked() {
+ setSurfaceInTransactionLocked(mSurface);
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = mName;
+ mInfo.width = mWidth;
+ mInfo.height = mHeight;
+ mInfo.refreshRate = mRefreshRate;
+ mInfo.flags = mFlags;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
+ mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
+ }
+ return mInfo;
+ }
+ }
+}
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
new file mode 100644
index 0000000..87e11e6
--- /dev/null
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -0,0 +1,820 @@
+/*
+ * 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 com.android.internal.util.DumpUtils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.display.WifiDisplay;
+import android.media.AudioManager;
+import android.media.RemoteDisplay;
+import android.net.NetworkInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pDeviceList;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.WifiP2pWfdInfo;
+import android.net.wifi.p2p.WifiP2pManager.ActionListener;
+import android.net.wifi.p2p.WifiP2pManager.Channel;
+import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener;
+import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
+import android.os.Handler;
+import android.util.Slog;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+/**
+ * Manages all of the various asynchronous interactions with the {@link WifiP2pManager}
+ * on behalf of {@link WifiDisplayAdapter}.
+ * <p>
+ * This code is isolated from {@link WifiDisplayAdapter} so that we can avoid
+ * accidentally introducing any deadlocks due to the display manager calling
+ * outside of itself while holding its lock. It's also way easier to write this
+ * asynchronous code if we can assume that it is single-threaded.
+ * </p><p>
+ * The controller must be instantiated on the handler thread.
+ * </p>
+ */
+final class WifiDisplayController implements DumpUtils.Dump {
+ private static final String TAG = "WifiDisplayController";
+ private static final boolean DEBUG = false;
+
+ private static final int DEFAULT_CONTROL_PORT = 7236;
+ private static final int MAX_THROUGHPUT = 50;
+ private static final int CONNECTION_TIMEOUT_SECONDS = 30;
+ private static final int RTSP_TIMEOUT_SECONDS = 15;
+
+ private static final int DISCOVER_PEERS_MAX_RETRIES = 10;
+ private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500;
+
+ private static final int CONNECT_MAX_RETRIES = 3;
+ private static final int CONNECT_RETRY_DELAY_MILLIS = 500;
+
+ // A unique token to identify the remote submix that is managed by Wifi display.
+ // It must match what the media server uses when it starts recording the submix
+ // for transmission. We use 0 although the actual value is currently ignored.
+ private static final int REMOTE_SUBMIX_ADDRESS = 0;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final Listener mListener;
+
+ private final WifiP2pManager mWifiP2pManager;
+ private final Channel mWifiP2pChannel;
+
+ private final AudioManager mAudioManager;
+
+ private boolean mWifiP2pEnabled;
+ private boolean mWfdEnabled;
+ private boolean mWfdEnabling;
+ private NetworkInfo mNetworkInfo;
+
+ private final ArrayList<WifiP2pDevice> mKnownWifiDisplayPeers =
+ new ArrayList<WifiP2pDevice>();
+
+ // True if there is a call to discoverPeers in progress.
+ private boolean mDiscoverPeersInProgress;
+
+ // Number of discover peers retries remaining.
+ private int mDiscoverPeersRetriesLeft;
+
+ // The device to which we want to connect, or null if we want to be disconnected.
+ private WifiP2pDevice mDesiredDevice;
+
+ // The device to which we are currently connecting, or null if we have already connected
+ // or are not trying to connect.
+ private WifiP2pDevice mConnectingDevice;
+
+ // The device to which we are currently connected, which means we have an active P2P group.
+ private WifiP2pDevice mConnectedDevice;
+
+ // The group info obtained after connecting.
+ private WifiP2pGroup mConnectedDeviceGroupInfo;
+
+ // Number of connection retries remaining.
+ private int mConnectionRetriesLeft;
+
+ // The remote display that is listening on the connection.
+ // Created after the Wifi P2P network is connected.
+ private RemoteDisplay mRemoteDisplay;
+
+ // The remote display interface.
+ private String mRemoteDisplayInterface;
+
+ // True if RTSP has connected.
+ private boolean mRemoteDisplayConnected;
+
+ // True if the remote submix is enabled.
+ private boolean mRemoteSubmixOn;
+
+ public WifiDisplayController(Context context, Handler handler, Listener listener) {
+ mContext = context;
+ mHandler = handler;
+ mListener = listener;
+
+ mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
+ mWifiP2pChannel = mWifiP2pManager.initialize(context, handler.getLooper(), null);
+
+ mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ context.registerReceiver(mWifiP2pReceiver, intentFilter);
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled);
+ pw.println("mWfdEnabled=" + mWfdEnabled);
+ pw.println("mWfdEnabling=" + mWfdEnabling);
+ pw.println("mNetworkInfo=" + mNetworkInfo);
+ pw.println("mDiscoverPeersInProgress=" + mDiscoverPeersInProgress);
+ pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft);
+ pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
+ pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
+ pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice));
+ pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft);
+ pw.println("mRemoteDisplay=" + mRemoteDisplay);
+ pw.println("mRemoteDisplayInterface=" + mRemoteDisplayInterface);
+ pw.println("mRemoteDisplayConnected=" + mRemoteDisplayConnected);
+ pw.println("mRemoteSubmixOn=" + mRemoteSubmixOn);
+
+ pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size());
+ for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
+ pw.println(" " + describeWifiP2pDevice(device));
+ }
+ }
+
+ public void requestScan() {
+ discoverPeers();
+ }
+
+ public void requestConnect(String address) {
+ for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
+ if (device.deviceAddress.equals(address)) {
+ connect(device);
+ }
+ }
+ }
+
+ public void requestDisconnect() {
+ disconnect();
+ }
+
+ private void enableWfd() {
+ if (!mWfdEnabled && !mWfdEnabling) {
+ mWfdEnabling = true;
+
+ WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
+ wfdInfo.setWfdEnabled(true);
+ wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE);
+ wfdInfo.setSessionAvailable(true);
+ wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
+ wfdInfo.setMaxThroughput(MAX_THROUGHPUT);
+ mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
+ @Override
+ public void onSuccess() {
+ if (DEBUG) {
+ Slog.d(TAG, "Successfully set WFD info.");
+ }
+ if (mWfdEnabling) {
+ mWfdEnabling = false;
+ setWfdEnabled(true);
+ }
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to set WFD info with reason " + reason + ".");
+ }
+ mWfdEnabling = false;
+ }
+ });
+ }
+ }
+
+ private void setWfdEnabled(final boolean enabled) {
+ if (mWfdEnabled != enabled) {
+ mWfdEnabled = enabled;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onEnablementChanged(enabled);
+ }
+ });
+ }
+ }
+
+ private void discoverPeers() {
+ if (!mDiscoverPeersInProgress) {
+ mDiscoverPeersInProgress = true;
+ mDiscoverPeersRetriesLeft = DISCOVER_PEERS_MAX_RETRIES;
+ handleScanStarted();
+ tryDiscoverPeers();
+ }
+ }
+
+ private void tryDiscoverPeers() {
+ mWifiP2pManager.discoverPeers(mWifiP2pChannel, new ActionListener() {
+ @Override
+ public void onSuccess() {
+ if (DEBUG) {
+ Slog.d(TAG, "Discover peers succeeded. Requesting peers now.");
+ }
+
+ mDiscoverPeersInProgress = false;
+ requestPeers();
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ if (DEBUG) {
+ Slog.d(TAG, "Discover peers failed with reason " + reason + ".");
+ }
+
+ if (mDiscoverPeersInProgress) {
+ if (reason == 0 && mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mDiscoverPeersInProgress) {
+ if (mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) {
+ mDiscoverPeersRetriesLeft -= 1;
+ if (DEBUG) {
+ Slog.d(TAG, "Retrying discovery. Retries left: "
+ + mDiscoverPeersRetriesLeft);
+ }
+ tryDiscoverPeers();
+ } else {
+ handleScanFinished();
+ mDiscoverPeersInProgress = false;
+ }
+ }
+ }
+ }, DISCOVER_PEERS_RETRY_DELAY_MILLIS);
+ } else {
+ handleScanFinished();
+ mDiscoverPeersInProgress = false;
+ }
+ }
+ }
+ });
+ }
+
+ private void requestPeers() {
+ mWifiP2pManager.requestPeers(mWifiP2pChannel, new PeerListListener() {
+ @Override
+ public void onPeersAvailable(WifiP2pDeviceList peers) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received list of peers.");
+ }
+
+ mKnownWifiDisplayPeers.clear();
+ for (WifiP2pDevice device : peers.getDeviceList()) {
+ if (DEBUG) {
+ Slog.d(TAG, " " + describeWifiP2pDevice(device));
+ }
+
+ if (isWifiDisplay(device)) {
+ mKnownWifiDisplayPeers.add(device);
+ }
+ }
+
+ handleScanFinished();
+ }
+ });
+ }
+
+ private void handleScanStarted() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onScanStarted();
+ }
+ });
+ }
+
+ private void handleScanFinished() {
+ final int count = mKnownWifiDisplayPeers.size();
+ final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
+ for (int i = 0; i < count; i++) {
+ displays[i] = createWifiDisplay(mKnownWifiDisplayPeers.get(i));
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onScanFinished(displays);
+ }
+ });
+ }
+
+ private void connect(final WifiP2pDevice device) {
+ if (mDesiredDevice != null
+ && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {
+ if (DEBUG) {
+ Slog.d(TAG, "connect: nothing to do, already connecting to "
+ + describeWifiP2pDevice(device));
+ }
+ return;
+ }
+
+ if (mConnectedDevice != null
+ && !mConnectedDevice.deviceAddress.equals(device.deviceAddress)
+ && mDesiredDevice == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "connect: nothing to do, already connected to "
+ + describeWifiP2pDevice(device) + " and not part way through "
+ + "connecting to a different device.");
+ }
+ return;
+ }
+
+ mDesiredDevice = device;
+ mConnectionRetriesLeft = CONNECT_MAX_RETRIES;
+ updateConnection();
+ }
+
+ private void disconnect() {
+ mDesiredDevice = null;
+ updateConnection();
+ }
+
+ private void retryConnection() {
+ if (mDesiredDevice != null && mConnectedDevice != mDesiredDevice
+ && mConnectionRetriesLeft > 0) {
+ mConnectionRetriesLeft -= 1;
+ Slog.i(TAG, "Retrying Wifi display connection. Retries left: "
+ + mConnectionRetriesLeft);
+
+ // Cheap hack. Make a new instance of the device object so that we
+ // can distinguish it from the previous connection attempt.
+ // This will cause us to tear everything down before we try again.
+ mDesiredDevice = new WifiP2pDevice(mDesiredDevice);
+ updateConnection();
+ }
+ }
+
+ /**
+ * This function is called repeatedly after each asynchronous operation
+ * until all preconditions for the connection have been satisfied and the
+ * connection is established (or not).
+ */
+ private void updateConnection() {
+ // Step 1. Before we try to connect to a new device, tell the system we
+ // have disconnected from the old one.
+ if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
+ Slog.i(TAG, "Stopped listening for RTSP connection on " + mRemoteDisplayInterface
+ + " from Wifi display: " + mConnectedDevice.deviceName);
+
+ mRemoteDisplay.dispose();
+ mRemoteDisplay = null;
+ mRemoteDisplayInterface = null;
+ mRemoteDisplayConnected = false;
+ mHandler.removeCallbacks(mRtspTimeout);
+
+ setRemoteSubmixOn(false);
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayDisconnected();
+ }
+ });
+
+ // continue to next step
+ }
+
+ // Step 2. Before we try to connect to a new device, disconnect from the old one.
+ if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
+ Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName);
+
+ final WifiP2pDevice oldDevice = mConnectedDevice;
+ mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
+ @Override
+ public void onSuccess() {
+ Slog.i(TAG, "Disconnected from Wifi display: " + oldDevice.deviceName);
+ next();
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ Slog.i(TAG, "Failed to disconnect from Wifi display: "
+ + oldDevice.deviceName + ", reason=" + reason);
+ next();
+ }
+
+ private void next() {
+ if (mConnectedDevice == oldDevice) {
+ mConnectedDevice = null;
+ updateConnection();
+ }
+ }
+ });
+ return; // wait for asynchronous callback
+ }
+
+ // Step 3. Before we try to connect to a new device, stop trying to connect
+ // to the old one.
+ if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
+ Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName);
+
+ mHandler.removeCallbacks(mConnectionTimeout);
+
+ final WifiP2pDevice oldDevice = mConnectingDevice;
+ mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() {
+ @Override
+ public void onSuccess() {
+ Slog.i(TAG, "Canceled connection to Wifi display: " + oldDevice.deviceName);
+ next();
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ Slog.i(TAG, "Failed to cancel connection to Wifi display: "
+ + oldDevice.deviceName + ", reason=" + reason);
+ next();
+ }
+
+ private void next() {
+ if (mConnectingDevice == oldDevice) {
+ mConnectingDevice = null;
+ updateConnection();
+ }
+ }
+ });
+ return; // wait for asynchronous callback
+ }
+
+ // Step 4. If we wanted to disconnect, then mission accomplished.
+ if (mDesiredDevice == null) {
+ return; // done
+ }
+
+ // Step 5. Try to connect.
+ if (mConnectedDevice == null && mConnectingDevice == null) {
+ Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
+
+ mConnectingDevice = mDesiredDevice;
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = mConnectingDevice.deviceAddress;
+
+ final WifiDisplay display = createWifiDisplay(mConnectingDevice);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayConnecting(display);
+ }
+ });
+
+ final WifiP2pDevice newDevice = mDesiredDevice;
+ mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
+ @Override
+ public void onSuccess() {
+ // The connection may not yet be established. We still need to wait
+ // for WIFI_P2P_CONNECTION_CHANGED_ACTION. However, we might never
+ // get that broadcast, so we register a timeout.
+ Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
+
+ mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ if (mConnectingDevice == newDevice) {
+ Slog.i(TAG, "Failed to initiate connection to Wifi display: "
+ + newDevice.deviceName + ", reason=" + reason);
+ mConnectingDevice = null;
+ handleConnectionFailure(false);
+ }
+ }
+ });
+ return; // wait for asynchronous callback
+ }
+
+ // Step 6. Listen for incoming connections.
+ if (mConnectedDevice != null && mRemoteDisplay == null) {
+ Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
+ if (addr == null) {
+ Slog.i(TAG, "Failed to get local interface address for communicating "
+ + "with Wifi display: " + mConnectedDevice.deviceName);
+ handleConnectionFailure(false);
+ return; // done
+ }
+
+ setRemoteSubmixOn(true);
+
+ final WifiP2pDevice oldDevice = mConnectedDevice;
+ final int port = getPortNumber(mConnectedDevice);
+ final String iface = addr.getHostAddress() + ":" + port;
+ mRemoteDisplayInterface = iface;
+
+ Slog.i(TAG, "Listening for RTSP connection on " + iface
+ + " from Wifi display: " + mConnectedDevice.deviceName);
+
+ mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
+ @Override
+ public void onDisplayConnected(final Surface surface,
+ final int width, final int height, final int flags) {
+ if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
+ Slog.i(TAG, "Opened RTSP connection with Wifi display: "
+ + mConnectedDevice.deviceName);
+ mRemoteDisplayConnected = true;
+ mHandler.removeCallbacks(mRtspTimeout);
+
+ final WifiDisplay display = createWifiDisplay(mConnectedDevice);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayConnected(display,
+ surface, width, height, flags);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onDisplayDisconnected() {
+ if (mConnectedDevice == oldDevice) {
+ Slog.i(TAG, "Closed RTSP connection with Wifi display: "
+ + mConnectedDevice.deviceName);
+ mHandler.removeCallbacks(mRtspTimeout);
+ disconnect();
+ }
+ }
+
+ @Override
+ public void onDisplayError(int error) {
+ if (mConnectedDevice == oldDevice) {
+ Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "
+ + error + ": " + mConnectedDevice.deviceName);
+ mHandler.removeCallbacks(mRtspTimeout);
+ handleConnectionFailure(false);
+ }
+ }
+ }, mHandler);
+
+ mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000);
+ }
+ }
+
+ private void setRemoteSubmixOn(boolean on) {
+ if (mRemoteSubmixOn != on) {
+ mRemoteSubmixOn = on;
+ mAudioManager.setRemoteSubmixOn(on, REMOTE_SUBMIX_ADDRESS);
+ }
+ }
+
+ private void handleStateChanged(boolean enabled) {
+ if (mWifiP2pEnabled != enabled) {
+ mWifiP2pEnabled = enabled;
+ if (enabled) {
+ if (!mWfdEnabled) {
+ enableWfd();
+ }
+ } else {
+ setWfdEnabled(false);
+ disconnect();
+ }
+ }
+ }
+
+ private void handlePeersChanged() {
+ if (mWifiP2pEnabled) {
+ if (mWfdEnabled) {
+ requestPeers();
+ } else {
+ enableWfd();
+ }
+ }
+ }
+
+ private void handleConnectionChanged(NetworkInfo networkInfo) {
+ mNetworkInfo = networkInfo;
+ if (mWfdEnabled && networkInfo.isConnected()) {
+ if (mDesiredDevice != null) {
+ mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() {
+ @Override
+ public void onGroupInfoAvailable(WifiP2pGroup info) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received group info: " + describeWifiP2pGroup(info));
+ }
+
+ if (mConnectingDevice != null && !info.contains(mConnectingDevice)) {
+ Slog.i(TAG, "Aborting connection to Wifi display because "
+ + "the current P2P group does not contain the device "
+ + "we expected to find: " + mConnectingDevice.deviceName);
+ handleConnectionFailure(false);
+ return;
+ }
+
+ if (mDesiredDevice != null && !info.contains(mDesiredDevice)) {
+ disconnect();
+ return;
+ }
+
+ if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
+ Slog.i(TAG, "Connected to Wifi display: "
+ + mConnectingDevice.deviceName);
+
+ mHandler.removeCallbacks(mConnectionTimeout);
+ mConnectedDeviceGroupInfo = info;
+ mConnectedDevice = mConnectingDevice;
+ mConnectingDevice = null;
+ updateConnection();
+ }
+ }
+ });
+ }
+ } else {
+ disconnect();
+
+ // After disconnection for a group, for some reason we have a tendency
+ // to get a peer change notification with an empty list of peers.
+ // Perform a fresh scan.
+ if (mWfdEnabled) {
+ requestPeers();
+ }
+ }
+ }
+
+ private final Runnable mConnectionTimeout = new Runnable() {
+ @Override
+ public void run() {
+ if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
+ Slog.i(TAG, "Timed out waiting for Wifi display connection after "
+ + CONNECTION_TIMEOUT_SECONDS + " seconds: "
+ + mConnectingDevice.deviceName);
+ handleConnectionFailure(true);
+ }
+ }
+ };
+
+ private final Runnable mRtspTimeout = new Runnable() {
+ @Override
+ public void run() {
+ if (mConnectedDevice != null
+ && mRemoteDisplay != null && !mRemoteDisplayConnected) {
+ Slog.i(TAG, "Timed out waiting for Wifi display RTSP connection after "
+ + RTSP_TIMEOUT_SECONDS + " seconds: "
+ + mConnectedDevice.deviceName);
+ handleConnectionFailure(true);
+ }
+ }
+ };
+
+ private void handleConnectionFailure(boolean timeoutOccurred) {
+ Slog.i(TAG, "Wifi display connection failed!");
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayConnectionFailed();
+ }
+ });
+
+ if (mDesiredDevice != null) {
+ if (mConnectionRetriesLeft > 0) {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ retryConnection();
+ }
+ }, timeoutOccurred ? 0 : CONNECT_RETRY_DELAY_MILLIS);
+ } else {
+ disconnect();
+ }
+ }
+ }
+
+ private static Inet4Address getInterfaceAddress(WifiP2pGroup info) {
+ NetworkInterface iface;
+ try {
+ iface = NetworkInterface.getByName(info.getInterface());
+ } catch (SocketException ex) {
+ Slog.w(TAG, "Could not obtain address of network interface "
+ + info.getInterface(), ex);
+ return null;
+ }
+
+ Enumeration<InetAddress> addrs = iface.getInetAddresses();
+ while (addrs.hasMoreElements()) {
+ InetAddress addr = addrs.nextElement();
+ if (addr instanceof Inet4Address) {
+ return (Inet4Address)addr;
+ }
+ }
+
+ Slog.w(TAG, "Could not obtain address of network interface "
+ + info.getInterface() + " because it had no IPv4 addresses.");
+ return null;
+ }
+
+ private static int getPortNumber(WifiP2pDevice device) {
+ if (device.deviceName.startsWith("DIRECT-")
+ && device.deviceName.endsWith("Broadcom")) {
+ // These dongles ignore the port we broadcast in our WFD IE.
+ return 8554;
+ }
+ return DEFAULT_CONTROL_PORT;
+ }
+
+ private static boolean isWifiDisplay(WifiP2pDevice device) {
+ return device.wfdInfo != null
+ && device.wfdInfo.isWfdEnabled()
+ && isPrimarySinkDeviceType(device.wfdInfo.getDeviceType());
+ }
+
+ private static boolean isPrimarySinkDeviceType(int deviceType) {
+ return deviceType == WifiP2pWfdInfo.PRIMARY_SINK
+ || deviceType == WifiP2pWfdInfo.SOURCE_OR_PRIMARY_SINK;
+ }
+
+ private static String describeWifiP2pDevice(WifiP2pDevice device) {
+ return device != null ? device.toString().replace('\n', ',') : "null";
+ }
+
+ private static String describeWifiP2pGroup(WifiP2pGroup group) {
+ return group != null ? group.toString().replace('\n', ',') : "null";
+ }
+
+ private static WifiDisplay createWifiDisplay(WifiP2pDevice device) {
+ return new WifiDisplay(device.deviceAddress, device.deviceName);
+ }
+
+ private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
+ boolean enabled = (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
+ WifiP2pManager.WIFI_P2P_STATE_DISABLED)) ==
+ WifiP2pManager.WIFI_P2P_STATE_ENABLED;
+ if (DEBUG) {
+ Slog.d(TAG, "Received WIFI_P2P_STATE_CHANGED_ACTION: enabled="
+ + enabled);
+ }
+
+ handleStateChanged(enabled);
+ } else if (action.equals(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received WIFI_P2P_PEERS_CHANGED_ACTION.");
+ }
+
+ handlePeersChanged();
+ } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
+ NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
+ WifiP2pManager.EXTRA_NETWORK_INFO);
+ if (DEBUG) {
+ Slog.d(TAG, "Received WIFI_P2P_CONNECTION_CHANGED_ACTION: networkInfo="
+ + networkInfo);
+ }
+
+ handleConnectionChanged(networkInfo);
+ }
+ }
+ };
+
+ /**
+ * Called on the handler thread when displays are connected or disconnected.
+ */
+ public interface Listener {
+ void onEnablementChanged(boolean enabled);
+
+ void onScanStarted();
+ void onScanFinished(WifiDisplay[] knownDisplays);
+
+ void onDisplayConnecting(WifiDisplay display);
+ void onDisplayConnectionFailed();
+ void onDisplayConnected(WifiDisplay display,
+ Surface surface, int width, int height, int flags);
+ void onDisplayDisconnected();
+ }
+}
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..805818a 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -19,6 +19,8 @@ package com.android.server.input;
import com.android.internal.R;
import com.android.internal.util.XmlUtils;
import com.android.server.Watchdog;
+import com.android.server.display.DisplayManagerService;
+import com.android.server.display.DisplayViewport;
import org.xmlpull.v1.XmlPullParser;
@@ -42,8 +44,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 +59,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;
@@ -89,7 +92,8 @@ import libcore.util.Objects;
/*
* Wraps the C++ InputManager and provides its callbacks.
*/
-public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
+public class InputManagerService extends IInputManager.Stub
+ implements Watchdog.Monitor, DisplayManagerService.InputManagerFuncs {
static final String TAG = "InputManager";
static final boolean DEBUG = false;
@@ -108,7 +112,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,17 +140,18 @@ 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,
Context context, MessageQueue messageQueue);
private static native void nativeStart(int ptr);
- private static native void nativeSetDisplaySize(int ptr, int displayId,
- int width, int height, int externalWidth, int externalHeight);
- private static native void nativeSetDisplayOrientation(int ptr, int displayId,
- int rotation, int externalRotation);
-
+ private static native void nativeSetDisplayViewport(int ptr, boolean external,
+ int displayId, int rotation,
+ int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
+ int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
+ int deviceWidth, int deviceHeight);
+
private static native int nativeGetScanCodeState(int ptr,
int deviceId, int sourceMask, int scanCode);
private static native int nativeGetKeyCodeState(int ptr,
@@ -236,11 +240,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;
@@ -282,29 +286,28 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
nativeReloadDeviceAliases(mPtr);
}
- public void setDisplaySize(int displayId, int width, int height,
- int externalWidth, int externalHeight) {
- if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
- throw new IllegalArgumentException("Invalid display id or dimensions.");
+ @Override
+ public void setDisplayViewports(DisplayViewport defaultViewport,
+ DisplayViewport externalTouchViewport) {
+ if (defaultViewport.valid) {
+ setDisplayViewport(false, defaultViewport);
}
-
- if (DEBUG) {
- Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height
- + " external size " + externalWidth + "x" + externalHeight);
+
+ if (externalTouchViewport.valid) {
+ setDisplayViewport(true, externalTouchViewport);
+ } else if (defaultViewport.valid) {
+ setDisplayViewport(true, defaultViewport);
}
- nativeSetDisplaySize(mPtr, displayId, width, height, externalWidth, externalHeight);
}
-
- public void setDisplayOrientation(int displayId, int rotation, int externalRotation) {
- if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
- throw new IllegalArgumentException("Invalid rotation.");
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation
- + " external rotation " + externalRotation);
- }
- nativeSetDisplayOrientation(mPtr, displayId, rotation, externalRotation);
+
+ private void setDisplayViewport(boolean external, DisplayViewport viewport) {
+ nativeSetDisplayViewport(mPtr, external,
+ viewport.displayId, viewport.orientation,
+ viewport.logicalFrame.left, viewport.logicalFrame.top,
+ viewport.logicalFrame.right, viewport.logicalFrame.bottom,
+ viewport.physicalFrame.left, viewport.physicalFrame.top,
+ viewport.physicalFrame.right, viewport.physicalFrame.bottom,
+ viewport.deviceWidth, viewport.deviceHeight);
}
/**
@@ -425,9 +428,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 +439,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 +1221,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 +1244,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 +1403,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 +1441,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 +1470,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/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
index 1026a0d..354858b 100755
--- a/services/java/com/android/server/location/ComprehensiveCountryDetector.java
+++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -384,8 +384,8 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
}
protected boolean isAirplaneModeOff() {
- return Settings.System.getInt(
- mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 0;
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 0;
}
/**
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..bb11fe7 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;
@@ -45,6 +47,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.provider.Settings;
import android.provider.Telephony.Carriers;
@@ -54,17 +57,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 +86,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 +159,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 +189,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 +220,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 +251,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 +267,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 +282,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 +298,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 +312,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 +340,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 +375,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 +383,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 +420,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 +431,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 +505,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 +553,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 +578,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 +693,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 +721,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 +733,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 +778,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 +849,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 +909,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
-
+
Binder.restoreCallingIdentity(identity);
return result;
}
@@ -992,18 +944,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 +990,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 +1006,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 +1027,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 +1054,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 +1062,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 +1083,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
@@ -1144,7 +1093,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// send an intent to notify that the GPS is receiving fixes.
Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
updateStatus(LocationProvider.AVAILABLE, mSvCount);
}
@@ -1161,7 +1110,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,24 +1148,10 @@ 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);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
}
@@ -1227,15 +1162,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 +1186,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") +
@@ -1268,7 +1203,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// send an intent to notify that the GPS is no longer receiving fixes.
Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
}
}
@@ -1339,7 +1274,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 +1321,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 +1481,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 +1504,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 +1519,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 +1542,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 +1594,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..84fd255
--- /dev/null
+++ b/services/java/com/android/server/location/LocationFudger.java
@@ -0,0 +1,397 @@
+/*
+ * 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.content.Context;
+import android.database.ContentObserver;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.provider.Settings;
+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";
+
+ /**
+ * Default coarse accuracy in meters.
+ */
+ private static final float DEFAULT_ACCURACY_IN_METERS = 2000.0f;
+
+ /**
+ * Minimum coarse accuracy in meters.
+ */
+ private static final float MINIMUM_ACCURACY_IN_METERS = 200.0f;
+
+ /**
+ * Secure settings key for coarse accuracy.
+ */
+ private static final String COARSE_ACCURACY_CONFIG_NAME = "locationCoarseAccuracy";
+
+ /**
+ * 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();
+
+ /**
+ * Used to monitor coarse accuracy secure setting for changes.
+ */
+ private final ContentObserver mSettingsObserver;
+
+ /**
+ * Used to resolve coarse accuracy setting.
+ */
+ private final Context mContext;
+
+ // all fields below protected by mLock
+ private double mOffsetLatitudeMeters;
+ private double mOffsetLongitudeMeters;
+ private long mNextInterval;
+
+ /**
+ * Best location accuracy allowed for coarse applications.
+ * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
+ */
+ private float mAccuracyInMeters;
+
+ /**
+ * The distance between grids for snap-to-grid. See {@link #createCoarse}.
+ * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
+ */
+ private double mGridSizeInMeters;
+
+ /**
+ * 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.
+ * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
+ */
+ private double mStandardDeviationInMeters;
+
+ public LocationFudger(Context context, Handler handler) {
+ mContext = context;
+ mSettingsObserver = new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ setAccuracyInMeters(loadCoarseAccuracy());
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ COARSE_ACCURACY_CONFIG_NAME), false, mSettingsObserver);
+
+ float accuracy = loadCoarseAccuracy();
+ synchronized (mLock) {
+ setAccuracyInMetersLocked(accuracy);
+ mOffsetLatitudeMeters = nextOffsetLocked();
+ mOffsetLongitudeMeters = nextOffsetLocked();
+ mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS;
+ }
+ }
+
+ /**
+ * Get the cached coarse location, or generate a new one and cache it.
+ */
+ public Location getOrCreate(Location location) {
+ synchronized (mLock) {
+ Bundle extras = location.getExtras();
+ if (extras == null) {
+ return addCoarseLocationExtraLocked(location);
+ }
+ Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
+ if (parcel == null) {
+ return addCoarseLocationExtraLocked(location);
+ }
+ if (!(parcel instanceof Location)) {
+ return addCoarseLocationExtraLocked(location);
+ }
+ Location coarse = (Location) parcel;
+ if (coarse.getAccuracy() < mAccuracyInMeters) {
+ return addCoarseLocationExtraLocked(location);
+ }
+ return coarse;
+ }
+ }
+
+ private Location addCoarseLocationExtraLocked(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) extras = new Bundle();
+ Location coarse = createCoarseLocked(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 createCoarseLocked(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.
+ 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(mGridSizeInMeters);
+ lat = Math.round(lat / latGranularity) * latGranularity;
+ double lonGranularity = metersToDegreesLongitude(mGridSizeInMeters, 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(mAccuracyInMeters, 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 * nextOffsetLocked();
+ mOffsetLongitudeMeters *= PREVIOUS_WEIGHT;
+ mOffsetLongitudeMeters += NEW_WEIGHT * nextOffsetLocked();
+
+ if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)",
+ mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+ }
+
+ private double nextOffsetLocked() {
+ return mRandom.nextGaussian() * mStandardDeviationInMeters;
+ }
+
+ 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));
+ }
+
+ /**
+ * This is the main control: call this to set the best location accuracy
+ * allowed for coarse applications and all derived values.
+ */
+ private void setAccuracyInMetersLocked(float accuracyInMeters) {
+ mAccuracyInMeters = Math.max(accuracyInMeters, MINIMUM_ACCURACY_IN_METERS);
+ if (D) {
+ Log.d(TAG, "setAccuracyInMetersLocked: new accuracy = " + mAccuracyInMeters);
+ }
+ mGridSizeInMeters = mAccuracyInMeters;
+ mStandardDeviationInMeters = mGridSizeInMeters / 4.0;
+ }
+
+ /**
+ * Same as setAccuracyInMetersLocked without the pre-lock requirement.
+ */
+ private void setAccuracyInMeters(float accuracyInMeters) {
+ synchronized (mLock) {
+ setAccuracyInMetersLocked(accuracyInMeters);
+ }
+ }
+
+ /**
+ * Loads the coarse accuracy value from secure settings.
+ */
+ private float loadCoarseAccuracy() {
+ String newSetting = Settings.Secure.getString(mContext.getContentResolver(),
+ COARSE_ACCURACY_CONFIG_NAME);
+ if (D) {
+ Log.d(TAG, "loadCoarseAccuracy: newSetting = \"" + newSetting + "\"");
+ }
+ if (newSetting == null) {
+ return DEFAULT_ACCURACY_IN_METERS;
+ }
+ try {
+ return Float.parseFloat(newSetting);
+ } catch (NumberFormatException e) {
+ return DEFAULT_ACCURACY_IN_METERS;
+ }
+ }
+}
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..f2d6745
--- /dev/null
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -0,0 +1,292 @@
+/*
+ * 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.EventLogTags;
+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 static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
+
+ 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 resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
+ resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 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) {
+ clearSourceRulesLocked();
+ mAcceptedEgressIface = null;
+ mVpn.stopLegacyVpn();
+ }
+ if (egressDisconnected) return;
+
+ final int egressType = egressInfo.getType();
+ if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
+ EventLogTags.writeLockdownVpnError(egressType);
+ }
+
+ 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");
+ EventLogTags.writeLockdownVpnConnecting(egressType);
+ 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);
+ EventLogTags.writeLockdownVpnConnected(egressType);
+ showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
+
+ try {
+ clearSourceRulesLocked();
+
+ 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() {
+ synchronized (mStateLock) {
+ initLocked();
+ }
+ }
+
+ private void initLocked() {
+ Slog.d(TAG, "initLocked()");
+
+ 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() {
+ synchronized (mStateLock) {
+ shutdownLocked();
+ }
+ }
+
+ private void shutdownLocked() {
+ Slog.d(TAG, "shutdownLocked()");
+
+ 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);
+ }
+ clearSourceRulesLocked();
+ hideNotification();
+
+ mContext.unregisterReceiver(mResetReceiver);
+ mVpn.setEnableNotifications(true);
+ }
+
+ public void reset() {
+ synchronized (mStateLock) {
+ // cycle tracker, reset error count, and trigger retry
+ shutdownLocked();
+ initLocked();
+ handleStateChangedLocked();
+ }
+ }
+
+ private void clearSourceRulesLocked() {
+ 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..43ddf8d 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -24,6 +24,8 @@ import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_UID_REMOVED;
+import static android.content.Intent.ACTION_USER_ADDED;
+import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -112,11 +114,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 +131,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;
@@ -178,7 +181,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int VERSION_ADDED_INFERRED = 7;
private static final int VERSION_SWITCH_APP_ID = 8;
private static final int VERSION_ADDED_NETWORK_ID = 9;
- private static final int VERSION_LATEST = VERSION_ADDED_NETWORK_ID;
+ private static final int VERSION_SWITCH_UID = 10;
+ private static final int VERSION_LATEST = VERSION_SWITCH_UID;
// @VisibleForTesting
public static final int TYPE_WARNING = 0x1;
@@ -249,8 +253,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Currently active network rules for ifaces. */
private HashMap<NetworkPolicy, String[]> mNetworkRules = Maps.newHashMap();
- /** Defined app policies. */
- private SparseIntArray mAppPolicy = new SparseIntArray();
+ /** Defined UID policies. */
+ private SparseIntArray mUidPolicy = new SparseIntArray();
/** Currently derived rules for each UID. */
private SparseIntArray mUidRules = new SparseIntArray();
@@ -356,12 +360,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
- // listen for package/uid changes to update policy
+ // listen for package changes to update policy
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_UID_REMOVED);
+ packageFilter.addDataScheme("package");
mContext.registerReceiver(mPackageReceiver, packageFilter, null, mHandler);
+ // listen for UID changes to update policy
+ mContext.registerReceiver(
+ mUidRemovedReceiver, new IntentFilter(ACTION_UID_REMOVED), null, mHandler);
+
+ // listen for user changes to update policy
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(ACTION_USER_ADDED);
+ userFilter.addAction(ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
+
// listen for stats update events
final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
mContext.registerReceiver(
@@ -420,33 +434,58 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // on background handler thread, and PACKAGE_ADDED and UID_REMOVED
- // are protected broadcasts.
+ // on background handler thread, and PACKAGE_ADDED is protected
final String action = intent.getAction();
- final int uid = intent.getIntExtra(EXTRA_UID, 0);
- final int appId = UserId.getAppId(uid);
+ final int uid = intent.getIntExtra(EXTRA_UID, -1);
+ if (uid == -1) return;
+
+ if (ACTION_PACKAGE_ADDED.equals(action)) {
+ // update rules for UID, since it might be subject to
+ // global background data policy
+ if (LOGV) Slog.v(TAG, "ACTION_PACKAGE_ADDED for uid=" + uid);
+ synchronized (mRulesLock) {
+ updateRulesForUidLocked(uid);
+ }
+ }
+ }
+ };
+
+ private BroadcastReceiver mUidRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and UID_REMOVED is protected
+
+ final int uid = intent.getIntExtra(EXTRA_UID, -1);
+ if (uid == -1) return;
+
+ // remove any policy and update rules to clean up
+ if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);
synchronized (mRulesLock) {
- if (ACTION_PACKAGE_ADDED.equals(action)) {
- // NOTE: PACKAGE_ADDED is currently only sent once, and is
- // not broadcast when users are added.
+ mUidPolicy.delete(uid);
+ updateRulesForUidLocked(uid);
+ writePolicyLocked();
+ }
+ }
+ };
- // update rules for UID, since it might be subject to
- // global background data policy.
- if (LOGV) Slog.v(TAG, "ACTION_PACKAGE_ADDED for uid=" + uid);
- updateRulesForAppLocked(appId);
+ private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and USER_ADDED and USER_REMOVED
+ // broadcasts are protected
- } else if (ACTION_UID_REMOVED.equals(action)) {
- // NOTE: UID_REMOVED is currently only sent once, and is not
- // broadcast when users are removed.
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId == -1) return;
- // remove any policy and update rules to clean up.
- if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);
+ // Remove any policies for given user; both cleaning up after a
+ // USER_REMOVED, and one last sanity check during USER_ADDED
+ removePoliciesForUserLocked(userId);
- mAppPolicy.delete(appId);
- updateRulesForAppLocked(appId);
- writePolicyLocked();
- }
+ // Update global restrict for new user
+ synchronized (mRulesLock) {
+ updateRulesForRestrictBackgroundLocked();
}
}
};
@@ -570,7 +609,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
@@ -787,11 +826,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(
- packageName, tag, 0x0, builder.getNotification(), idReceived);
+ packageName, tag, 0x0, builder.getNotification(), idReceived,
+ UserHandle.USER_OWNER);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -821,11 +862,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(packageName, tag,
- 0x0, builder.getNotification(), idReceived);
+ 0x0, builder.getNotification(), idReceived, UserHandle.USER_OWNER);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -834,10 +876,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void cancelNotification(String tag) {
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
mNotifManager.cancelNotificationWithTag(
- packageName, tag, 0x0);
+ packageName, tag, 0x0, UserHandle.USER_OWNER);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -1102,7 +1145,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// clear any existing policy and read from disk
mNetworkPolicy.clear();
- mAppPolicy.clear();
+ mUidPolicy.clear();
FileInputStream fis = null;
try {
@@ -1183,24 +1226,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
cycleTimezone, warningBytes, limitBytes, lastWarningSnooze,
lastLimitSnooze, metered, inferred));
- } else if (TAG_UID_POLICY.equals(tag) && version < VERSION_SWITCH_APP_ID) {
+ } else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
final int policy = readIntAttribute(in, ATTR_POLICY);
- final int appId = UserId.getAppId(uid);
- if (UserId.isApp(appId)) {
- setAppPolicyUnchecked(appId, policy, false);
+ if (UserHandle.isApp(uid)) {
+ setUidPolicyUnchecked(uid, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
- } else if (TAG_APP_POLICY.equals(tag) && version >= VERSION_SWITCH_APP_ID) {
+ } else if (TAG_APP_POLICY.equals(tag)) {
final int appId = readIntAttribute(in, ATTR_APP_ID);
final int policy = readIntAttribute(in, ATTR_POLICY);
- if (UserId.isApp(appId)) {
- setAppPolicyUnchecked(appId, policy, false);
+ // TODO: set for other users during upgrade
+ final int uid = UserHandle.getUid(UserHandle.USER_OWNER, appId);
+ if (UserHandle.isApp(uid)) {
+ setUidPolicyUnchecked(uid, policy, false);
} else {
- Slog.w(TAG, "unable to apply policy to appId " + appId + "; ignoring");
+ Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
}
}
@@ -1230,7 +1274,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (mRestrictBackground) {
final Intent broadcast = new Intent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
- mContext.sendBroadcast(broadcast);
+ mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
}
}
@@ -1275,17 +1319,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
// write all known uid policies
- for (int i = 0; i < mAppPolicy.size(); i++) {
- final int appId = mAppPolicy.keyAt(i);
- final int policy = mAppPolicy.valueAt(i);
+ for (int i = 0; i < mUidPolicy.size(); i++) {
+ final int uid = mUidPolicy.keyAt(i);
+ final int policy = mUidPolicy.valueAt(i);
// skip writing empty policies
if (policy == POLICY_NONE) continue;
- out.startTag(null, TAG_APP_POLICY);
- writeIntAttribute(out, ATTR_APP_ID, appId);
+ out.startTag(null, TAG_UID_POLICY);
+ writeIntAttribute(out, ATTR_UID, uid);
writeIntAttribute(out, ATTR_POLICY, policy);
- out.endTag(null, TAG_APP_POLICY);
+ out.endTag(null, TAG_UID_POLICY);
}
out.endTag(null, TAG_POLICY_LIST);
@@ -1300,24 +1344,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
- public void setAppPolicy(int appId, int policy) {
+ public void setUidPolicy(int uid, int policy) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- if (!UserId.isApp(appId)) {
- throw new IllegalArgumentException("cannot apply policy to appId " + appId);
+ if (!UserHandle.isApp(uid)) {
+ throw new IllegalArgumentException("cannot apply policy to UID " + uid);
}
- setAppPolicyUnchecked(appId, policy, true);
+ setUidPolicyUnchecked(uid, policy, true);
}
- private void setAppPolicyUnchecked(int appId, int policy, boolean persist) {
+ private void setUidPolicyUnchecked(int uid, int policy, boolean persist) {
final int oldPolicy;
synchronized (mRulesLock) {
- oldPolicy = getAppPolicy(appId);
- mAppPolicy.put(appId, policy);
+ oldPolicy = getUidPolicy(uid);
+ mUidPolicy.put(uid, policy);
// uid policy changed, recompute rules and persist policy.
- updateRulesForAppLocked(appId);
+ updateRulesForUidLocked(uid);
if (persist) {
writePolicyLocked();
}
@@ -1325,29 +1369,53 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
- public int getAppPolicy(int appId) {
+ public int getUidPolicy(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
synchronized (mRulesLock) {
- return mAppPolicy.get(appId, POLICY_NONE);
+ return mUidPolicy.get(uid, POLICY_NONE);
}
}
@Override
- public int[] getAppsWithPolicy(int policy) {
+ public int[] getUidsWithPolicy(int policy) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- int[] appIds = new int[0];
+ int[] uids = new int[0];
synchronized (mRulesLock) {
- for (int i = 0; i < mAppPolicy.size(); i++) {
- final int appId = mAppPolicy.keyAt(i);
- final int appPolicy = mAppPolicy.valueAt(i);
- if (appPolicy == policy) {
- appIds = appendInt(appIds, appId);
+ for (int i = 0; i < mUidPolicy.size(); i++) {
+ final int uid = mUidPolicy.keyAt(i);
+ final int uidPolicy = mUidPolicy.valueAt(i);
+ if (uidPolicy == policy) {
+ uids = appendInt(uids, uid);
}
}
}
- return appIds;
+ return uids;
+ }
+
+ /**
+ * Remove any policies associated with given {@link UserHandle}, persisting
+ * if any changes are made.
+ */
+ private void removePoliciesForUserLocked(int userId) {
+ if (LOGV) Slog.v(TAG, "removePoliciesForUserLocked()");
+
+ int[] uids = new int[0];
+ for (int i = 0; i < mUidPolicy.size(); i++) {
+ final int uid = mUidPolicy.keyAt(i);
+ if (UserHandle.getUserId(uid) == userId) {
+ uids = appendInt(uids, uid);
+ }
+ }
+
+ if (uids.length > 0) {
+ for (int uid : uids) {
+ mUidPolicy.delete(uid);
+ updateRulesForUidLocked(uid);
+ }
+ writePolicyLocked();
+ }
}
@Override
@@ -1581,14 +1649,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
fout.decreaseIndent();
- fout.println("Policy for apps:");
+ fout.println("Policy for UIDs:");
fout.increaseIndent();
- int size = mAppPolicy.size();
+ int size = mUidPolicy.size();
for (int i = 0; i < size; i++) {
- final int appId = mAppPolicy.keyAt(i);
- final int policy = mAppPolicy.valueAt(i);
- fout.print("appId=");
- fout.print(appId);
+ final int uid = mUidPolicy.keyAt(i);
+ final int policy = mUidPolicy.valueAt(i);
+ fout.print("UID=");
+ fout.print(uid);
fout.print(" policy=");
dumpPolicy(fout, policy);
fout.println();
@@ -1693,12 +1761,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* Update rules that might be changed by {@link #mRestrictBackground} value.
*/
private void updateRulesForRestrictBackgroundLocked() {
- // update rules for all installed applications
final PackageManager pm = mContext.getPackageManager();
- final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
- for (ApplicationInfo app : apps) {
- final int appId = UserId.getAppId(app.uid);
- updateRulesForAppLocked(appId);
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+ // update rules for all installed applications
+ final List<UserInfo> users = um.getUsers();
+ final List<ApplicationInfo> apps = pm.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS);
+
+ for (UserInfo user : users) {
+ for (ApplicationInfo app : apps) {
+ final int uid = UserHandle.getUid(user.id, app.uid);
+ updateRulesForUidLocked(uid);
+ }
}
// limit data usage for some internal system services
@@ -1706,17 +1781,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
updateRulesForUidLocked(android.os.Process.DRM_UID);
}
- private void updateRulesForAppLocked(int appId) {
- for (UserInfo user : mContext.getPackageManager().getUsers()) {
- final int uid = UserId.getUid(user.id, appId);
- updateRulesForUidLocked(uid);
- }
- }
-
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,13 +1794,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void updateRulesForUidLocked(int uid) {
if (!isUidValidForRules(uid)) return;
- final int appId = UserId.getAppId(uid);
- final int appPolicy = getAppPolicy(appId);
+ final int uidPolicy = getUidPolicy(uid);
final boolean uidForeground = isUidForeground(uid);
// derive active rules based on policy and active state
int uidRules = RULE_ALLOW_ALL;
- if (!uidForeground && (appPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
+ if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
// uid in background, and policy says to block metered data
uidRules = RULE_REJECT_METERED;
}
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..3a593e4 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -38,23 +38,23 @@ import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE;
-import static android.provider.Settings.Secure.NETSTATS_DEV_PERSIST_BYTES;
-import static android.provider.Settings.Secure.NETSTATS_DEV_ROTATE_AGE;
-import static android.provider.Settings.Secure.NETSTATS_GLOBAL_ALERT_BYTES;
-import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
-import static android.provider.Settings.Secure.NETSTATS_REPORT_XT_OVER_DEV;
-import static android.provider.Settings.Secure.NETSTATS_SAMPLE_ENABLED;
-import static android.provider.Settings.Secure.NETSTATS_TIME_CACHE_MAX_AGE;
-import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_UID_DELETE_AGE;
-import static android.provider.Settings.Secure.NETSTATS_UID_PERSIST_BYTES;
-import static android.provider.Settings.Secure.NETSTATS_UID_ROTATE_AGE;
-import static android.provider.Settings.Secure.NETSTATS_UID_TAG_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_UID_TAG_DELETE_AGE;
-import static android.provider.Settings.Secure.NETSTATS_UID_TAG_PERSIST_BYTES;
-import static android.provider.Settings.Secure.NETSTATS_UID_TAG_ROTATE_AGE;
+import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION;
+import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE;
+import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES;
+import static android.provider.Settings.Global.NETSTATS_DEV_ROTATE_AGE;
+import static android.provider.Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES;
+import static android.provider.Settings.Global.NETSTATS_POLL_INTERVAL;
+import static android.provider.Settings.Global.NETSTATS_REPORT_XT_OVER_DEV;
+import static android.provider.Settings.Global.NETSTATS_SAMPLE_ENABLED;
+import static android.provider.Settings.Global.NETSTATS_TIME_CACHE_MAX_AGE;
+import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
+import static android.provider.Settings.Global.NETSTATS_UID_DELETE_AGE;
+import static android.provider.Settings.Global.NETSTATS_UID_PERSIST_BYTES;
+import static android.provider.Settings.Global.NETSTATS_UID_ROTATE_AGE;
+import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION;
+import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE;
+import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES;
+import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE;
import static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -99,6 +99,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.telephony.PhoneStateListener;
@@ -763,7 +764,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
@@ -989,7 +990,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// finally, dispatch updated event to any listeners
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(updatedIntent, READ_NETWORK_USAGE_HISTORY);
+ mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
+ READ_NETWORK_USAGE_HISTORY);
}
/**
@@ -1216,39 +1218,39 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: adjust these timings for production builds
}
- private long getSecureLong(String name, long def) {
- return Settings.Secure.getLong(mResolver, name, def);
+ private long getGlobalLong(String name, long def) {
+ return Settings.Global.getLong(mResolver, name, def);
}
- private boolean getSecureBoolean(String name, boolean def) {
+ private boolean getGlobalBoolean(String name, boolean def) {
final int defInt = def ? 1 : 0;
- return Settings.Secure.getInt(mResolver, name, defInt) != 0;
+ return Settings.Global.getInt(mResolver, name, defInt) != 0;
}
@Override
public long getPollInterval() {
- return getSecureLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
+ return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
}
@Override
public long getTimeCacheMaxAge() {
- return getSecureLong(NETSTATS_TIME_CACHE_MAX_AGE, DAY_IN_MILLIS);
+ return getGlobalLong(NETSTATS_TIME_CACHE_MAX_AGE, DAY_IN_MILLIS);
}
@Override
public long getGlobalAlertBytes(long def) {
- return getSecureLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
+ return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
}
@Override
public boolean getSampleEnabled() {
- return getSecureBoolean(NETSTATS_SAMPLE_ENABLED, true);
+ return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true);
}
@Override
public boolean getReportXtOverDev() {
- return getSecureBoolean(NETSTATS_REPORT_XT_OVER_DEV, true);
+ return getGlobalBoolean(NETSTATS_REPORT_XT_OVER_DEV, true);
}
@Override
public Config getDevConfig() {
- return new Config(getSecureLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
- getSecureLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
- getSecureLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
+ return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
+ getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
+ getGlobalLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
}
@Override
public Config getXtConfig() {
@@ -1256,19 +1258,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
public Config getUidConfig() {
- return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
- getSecureLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
- getSecureLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
+ return new Config(getGlobalLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
+ getGlobalLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
+ getGlobalLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
}
@Override
public Config getUidTagConfig() {
- return new Config(getSecureLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
- getSecureLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS),
- getSecureLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS));
+ return new Config(getGlobalLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
+ getGlobalLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS),
+ getGlobalLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS));
}
@Override
public long getDevPersistBytes(long def) {
- return getSecureLong(NETSTATS_DEV_PERSIST_BYTES, def);
+ return getGlobalLong(NETSTATS_DEV_PERSIST_BYTES, def);
}
@Override
public long getXtPersistBytes(long def) {
@@ -1276,11 +1278,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
public long getUidPersistBytes(long def) {
- return getSecureLong(NETSTATS_UID_PERSIST_BYTES, def);
+ return getGlobalLong(NETSTATS_UID_PERSIST_BYTES, def);
}
@Override
public long getUidTagPersistBytes(long def) {
- return getSecureLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
+ return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
}
}
}
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 48004bb..ad85c0d 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -25,7 +25,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-class Installer {
+public final class Installer {
private static final String TAG = "Installer";
private static final boolean LOCAL_DEBUG = false;
@@ -324,26 +324,14 @@ class Installer {
return execute(builder.toString());
}
- /*
- * @param packagePathSuffix The name of the path relative to install
- * directory. Say if the path name is /data/app/com.test-1.apk, the package
- * suffix path will be com.test-1
- */
- public int setForwardLockPerm(String packagePathSuffix, int gid) {
- StringBuilder builder = new StringBuilder("protect");
- builder.append(' ');
- builder.append(packagePathSuffix);
- builder.append(' ');
- builder.append(gid);
- 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 : "!");
@@ -371,12 +359,20 @@ class Installer {
return execute("movefiles");
}
+ /**
+ * Links the native library directory in an application's directory to its
+ * real location.
+ *
+ * @param dataPath data directory where the application is
+ * @param nativeLibPath target native library path
+ * @return -1 on error
+ */
public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath) {
if (dataPath == null) {
- Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
+ Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
return -1;
} else if (nativeLibPath == null) {
- Slog.e(TAG, "unlinkNativeLibraryDirectory nativeLibPath is null");
+ Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
return -1;
}
@@ -387,16 +383,4 @@ class Installer {
return execute(builder.toString());
}
-
- public int unlinkNativeLibraryDirectory(String dataPath) {
- if (dataPath == null) {
- Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
- return -1;
- }
-
- StringBuilder builder = new StringBuilder("unlinklib ");
- builder.append(dataPath);
-
- return execute(builder.toString());
- }
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index f914271..536c612 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -25,6 +25,11 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.ArrayUtils.removeInt;
import static libcore.io.OsConstants.S_ISLNK;
+import static libcore.io.OsConstants.S_IRWXU;
+import static libcore.io.OsConstants.S_IRGRP;
+import static libcore.io.OsConstants.S_IXGRP;
+import static libcore.io.OsConstants.S_IROTH;
+import static libcore.io.OsConstants.S_IXOTH;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
@@ -64,10 +69,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 +84,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 +108,8 @@ 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.os.Environment.UserEnvironment;
import android.provider.Settings.Secure;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
@@ -144,6 +153,7 @@ import java.util.Set;
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
+import libcore.io.OsConstants;
import libcore.io.StructStat;
/**
@@ -165,6 +175,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 +186,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;
@@ -203,14 +215,21 @@ public class PackageManagerService extends IPackageManager.Stub {
/**
* Whether verification is enabled by default.
*/
- // STOPSHIP: change this to true
- private static final boolean DEFAULT_VERIFY_ENABLE = false;
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
/**
* 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";
@@ -264,7 +283,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// This is the object monitoring mDrmAppPrivateInstallDir.
final FileObserver mDrmAppInstallObserver;
- // Used for priviledge escalation. MUST NOT BE CALLED WITH mPackages
+ // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
// LOCK HELD. Can be called with mInstallLock held.
final Installer mInstaller;
@@ -274,6 +293,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final File mAppInstallDir;
final File mDalvikCacheDir;
+ /**
+ * Directory to which applications installed internally have native
+ * libraries copied.
+ */
+ private File mAppLibInstallDir;
+
// Directory containing the private parts (e.g. code and non-resource assets) of forward-locked
// apps.
final File mDrmAppPrivateInstallDir;
@@ -388,6 +413,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 +436,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 +482,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 +567,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 +692,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);
@@ -674,26 +722,65 @@ public class PackageManagerService extends IPackageManager.Stub {
res.removedInfo.sendBroadcast(false, true);
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
+ // Determine the set of users who are adding this
+ // package for the first time vs. those who are seeing
+ // an update.
+ int[] firstUsers;
+ int[] updateUsers = new int[0];
+ if (res.origUsers == null || res.origUsers.length == 0) {
+ firstUsers = res.newUsers;
+ } else {
+ firstUsers = new int[0];
+ for (int i=0; i<res.newUsers.length; i++) {
+ int user = res.newUsers[i];
+ boolean isNew = true;
+ for (int j=0; j<res.origUsers.length; j++) {
+ if (res.origUsers[j] == user) {
+ isNew = false;
+ break;
+ }
+ }
+ if (isNew) {
+ int[] newFirst = new int[firstUsers.length+1];
+ System.arraycopy(firstUsers, 0, newFirst, 0,
+ firstUsers.length);
+ newFirst[firstUsers.length] = user;
+ firstUsers = newFirst;
+ } else {
+ int[] newUpdate = new int[updateUsers.length+1];
+ System.arraycopy(updateUsers, 0, newUpdate, 0,
+ updateUsers.length);
+ newUpdate[updateUsers.length] = user;
+ updateUsers = newUpdate;
+ }
+ }
+ }
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ res.pkg.applicationInfo.packageName,
+ extras, null, null, firstUsers);
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, updateUsers);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
- res.pkg.applicationInfo.packageName, null,
- UserId.USER_ALL);
+ res.pkg.applicationInfo.packageName, null, updateUsers);
}
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 +851,33 @@ 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);
+ broadcastPackageVerified(verificationId, args.packageURI,
+ PackageManager.VERIFICATION_ALLOW);
+ try {
+ ret = args.copyApk(mContainerService, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not contact the ContainerService");
+ }
+ } else {
+ broadcastPackageVerified(verificationId, args.packageURI,
+ PackageManager.VERIFICATION_REJECT);
+ }
+ processPendingInstall(args, ret);
mHandler.sendEmptyMessage(MCS_UNBIND);
}
-
break;
}
case PACKAGE_VERIFIED: {
@@ -798,6 +901,8 @@ public class PackageManagerService extends IPackageManager.Stub {
int ret;
if (state.isInstallAllowed()) {
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ broadcastPackageVerified(verificationId, args.packageURI,
+ response.code);
try {
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
@@ -832,9 +937,10 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- public static final IPackageManager main(Context context, boolean factoryTest,
- boolean onlyCore) {
- PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
+ public static final IPackageManager main(Context context, Installer installer,
+ boolean factoryTest, boolean onlyCore) {
+ PackageManagerService m = new PackageManagerService(context, installer,
+ factoryTest, onlyCore);
ServiceManager.addService("package", m);
return m;
}
@@ -861,7 +967,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return res;
}
- public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore) {
+ public PackageManagerService(Context context, Installer installer,
+ boolean factoryTest, boolean onlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
@@ -874,12 +981,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) {
@@ -898,7 +1006,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mSeparateProcesses = null;
}
- mInstaller = new Installer();
+ mInstaller = installer;
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
@@ -916,11 +1024,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,
@@ -1099,7 +1208,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
Slog.i(TAG, "Expecting better updatd system app for " + ps.name
+ "; removing system app");
- removePackageLI(scannedPkg, true);
+ removePackageLI(ps, true);
}
continue;
@@ -1110,8 +1219,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()) {
@@ -1122,6 +1230,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mAppInstallDir = new File(dataDir, "app");
+ mAppLibInstallDir = new File(dataDir, "app-lib");
//look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//clean up list
@@ -1160,9 +1269,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 +1404,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 +1634,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;
@@ -1556,6 +1655,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get package info");
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1598,18 +1698,19 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public int getPackageUid(String packageName, int userId) {
if (!sUserManager.exists(userId)) return -1;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get package uid");
// reader
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 +1813,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 +1831,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 =
- getDataPathForPackage(ps.pkg.packageName, 0).getPath();
- ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
- }
- // ps.pkg.mSetEnabled = ps.getEnabled(userId);
- // ps.pkg.mSetStopped = ps.getStopped(userId);
- return generatePackageInfo(ps.pkg, flags, userId);
+ 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 | ApplicationInfo.FLAG_IS_DATA_ONLY;
+ pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+ pkg.applicationInfo.sourceDir = ps.codePathString;
+ pkg.applicationInfo.dataDir =
+ getDataPathForPackage(packageName, 0).getPath();
+ pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+ }
+ // pkg.mSetEnabled = ps.getEnabled(userId);
+ // pkg.mSetStopped = ps.getStopped(userId);
+ return generatePackageInfo(pkg, flags, userId);
}
return null;
}
@@ -1750,6 +1855,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get application info");
// writer
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1760,13 +1866,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 +1887,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 +1912,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 {
@@ -1826,6 +1935,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get activity info");
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -1833,8 +1943,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;
@@ -1846,6 +1956,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -1853,8 +1964,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;
@@ -1863,6 +1974,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -1870,8 +1982,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;
@@ -1880,6 +1992,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -1887,8 +2000,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 +2046,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 +2075,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)) {
@@ -1981,6 +2094,34 @@ public class PackageManagerService extends IPackageManager.Stub {
return PackageManager.PERMISSION_DENIED;
}
+ /**
+ * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
+ * @param message the message to log on security exception
+ * @return
+ */
+ private void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (userId == UserHandle.getUserId(callingUid)) return;
+ if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ if (requireFullPermission) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } else {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } catch (SecurityException se) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, message);
+ }
+ }
+ }
+ }
+
private BasePermission findPermissionTreeLP(String permName) {
for(BasePermission bp : mSettings.mPermissionTrees.values()) {
if (permName.startsWith(bp.name) &&
@@ -1996,7 +2137,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 +2340,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 +2399,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 +2424,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;
@@ -2313,6 +2454,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "resolve intent");
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
return chooseBestActivity(intent, resolvedType, flags, query, userId);
}
@@ -2394,6 +2536,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;
}
@@ -2451,6 +2596,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities");
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2490,6 +2636,8 @@ public class PackageManagerService extends IPackageManager.Stub {
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
+ "query intent activity options");
final String resultsAction = intent.getAction();
List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
@@ -2759,11 +2907,14 @@ public class PackageManagerService extends IPackageManager.Stub {
return index;
}
- public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, String lastRead) {
+ @Override
+ public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, String lastRead,
+ int userId) {
final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>();
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
final String[] keys;
- int userId = UserId.getCallingUserId();
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "get installed packages");
// writer
synchronized (mPackages) {
@@ -2838,8 +2989,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 +3013,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 +3043,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 +3062,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 +3090,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 +3141,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 +3172,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 +3240,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 +3306,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 +3340,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 +3362,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 +3393,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
@@ -3378,8 +3535,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return DEX_OPT_DEFERRED;
} else {
Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
- ret = mInstaller.dexopt(path, pkg.applicationInfo.uid,
- !isForwardLocked(pkg));
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg));
pkg.mDidDexOpt = true;
performed = true;
}
@@ -3439,8 +3596,45 @@ 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;
+ }
+ }
+
+ final File nativeLibraryFile = new File(mAppLibInstallDir, packageName);
+ NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
+ if (!nativeLibraryFile.delete()) {
+ Slog.w(TAG, "Couldn't delete native library directory " + nativeLibraryFile.getPath());
+ }
+
+ 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 +3826,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 +3985,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 +3999,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 +4008,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 +4045,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();
@@ -3886,9 +4071,7 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) {
if (pkgSetting.nativeLibraryPathString == null) {
- final String nativeLibraryPath = new File(dataPath, LIB_DIR_NAME).getPath();
- pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
- pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+ setInternalAppNativeLibraryPath(pkg, pkgSetting);
} else {
pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
}
@@ -3910,7 +4093,7 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
try {
- final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+ File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
final String dataPathString = dataPath.getCanonicalPath();
if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
@@ -3925,37 +4108,43 @@ public class PackageManagerService extends IPackageManager.Stub {
Log.i(TAG, "removed obsolete native libraries for system package "
+ path);
}
- } else if (nativeLibraryDir.getParentFile().getCanonicalPath()
- .equals(dataPathString)) {
- /*
- * Make sure the native library dir isn't a symlink to
- * something. If it is, ask installd to remove it and create
- * a directory so we can copy to it afterwards.
- */
- boolean isSymLink;
- try {
- isSymLink = S_ISLNK(Libcore.os.lstat(nativeLibraryDir.getPath()).st_mode);
- } catch (ErrnoException e) {
- // This shouldn't happen, but we'll fail-safe.
- isSymLink = true;
+ } else if (!isForwardLocked(pkg) && !isExternal(pkg)) {
+ // Update native library dir if it starts with /data/data
+ if (nativeLibraryDir.getPath().startsWith(dataPathString)) {
+ setInternalAppNativeLibraryPath(pkg, pkgSetting);
+ nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
}
- if (isSymLink) {
- mInstaller.unlinkNativeLibraryDirectory(dataPathString);
+
+ try {
+ if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {
+ Slog.e(TAG, "Unable to copy native libraries");
+ mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ return null;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to copy native libraries", e);
+ mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ return null;
}
- /*
- * If this is an internal application or our
- * nativeLibraryPath points to our data directory, unpack
- * the libraries if necessary.
- */
- NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
+ if (mInstaller.linkNativeLibraryDirectory(dataPathString,
+ pkg.applicationInfo.nativeLibraryDir) == -1) {
+ Slog.e(TAG, "Unable to link native library directory");
+ mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ return null;
+ }
} else {
Slog.i(TAG, "Linking native library dir for " + path);
- mInstaller.linkNativeLibraryDirectory(dataPathString,
+ int ret = mInstaller.linkNativeLibraryDirectory(dataPathString,
pkg.applicationInfo.nativeLibraryDir);
+ if (ret < 0) {
+ Slog.w(TAG, "Failed linking native library dir for " + path);
+ mLastScanError = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ return null;
+ }
}
} catch (IOException ioe) {
- Log.e(TAG, "Unable to get canonical file " + ioe.toString());
+ Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
}
}
pkg.mScanPath = path;
@@ -3992,7 +4181,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) {
@@ -4268,20 +4459,75 @@ public class PackageManagerService extends IPackageManager.Stub {
return pkg;
}
- private void killApplication(String pkgName, int uid) {
+ private void setInternalAppNativeLibraryPath(PackageParser.Package pkg,
+ PackageSetting pkgSetting) {
+ final String apkLibPath = getApkName(pkgSetting.codePathString);
+ final String nativeLibraryPath = new File(mAppLibInstallDir, apkLibPath).getPath();
+ pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
+ pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+ }
+
+ private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir)
+ throws IOException {
+ if (!nativeLibraryDir.isDirectory()) {
+ nativeLibraryDir.delete();
+
+ if (!nativeLibraryDir.mkdir()) {
+ throw new IOException("Cannot create " + nativeLibraryDir.getPath());
+ }
+
+ try {
+ Libcore.os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH
+ | S_IXOTH);
+ } catch (ErrnoException e) {
+ throw new IOException("Cannot chmod native library directory "
+ + nativeLibraryDir.getPath(), e);
+ }
+ } else if (!SELinux.restorecon(nativeLibraryDir)) {
+ throw new IOException("Cannot set SELinux context for " + nativeLibraryDir.getPath());
+ }
+
+ /*
+ * If this is an internal application or our nativeLibraryPath points to
+ * the app-lib directory, unpack the libraries if necessary.
+ */
+ return NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
+ }
+
+ private void killApplication(String pkgName, int appId) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- am.killApplicationWithUid(pkgName, uid);
+ am.killApplicationWithAppId(pkgName, appId);
} catch (RemoteException e) {
}
}
}
- void removePackageLI(PackageParser.Package pkg, boolean chatty) {
+ void removePackageLI(PackageSetting ps, boolean chatty) {
+ if (DEBUG_INSTALL) {
+ if (chatty)
+ Log.d(TAG, "Removing package " + ps.name);
+ }
+
+ // writer
+ synchronized (mPackages) {
+ mPackages.remove(ps.name);
+ if (ps.codePathString != null) {
+ mAppDirs.remove(ps.codePathString);
+ }
+
+ final PackageParser.Package pkg = ps.pkg;
+ if (pkg != null) {
+ cleanPackageDataStructuresLILPw(pkg, chatty);
+ }
+ }
+ }
+
+ void removeInstalledPackageLI(PackageParser.Package pkg, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName);
@@ -4293,143 +4539,146 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkg.mPath != null) {
mAppDirs.remove(pkg.mPath);
}
+ cleanPackageDataStructuresLILPw(pkg, chatty);
+ }
+ }
- int N = pkg.providers.size();
- StringBuilder r = null;
- int i;
- for (i=0; i<N; i++) {
- PackageParser.Provider p = pkg.providers.get(i);
- mProvidersByComponent.remove(new ComponentName(p.info.packageName,
- p.info.name));
- if (p.info.authority == null) {
-
- /* The is another ContentProvider with this authority when
- * this app was installed so this authority is null,
- * Ignore it as we don't have to unregister the provider.
- */
- continue;
- }
- String names[] = p.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- if (mProviders.get(names[j]) == p) {
- mProviders.remove(names[j]);
- if (DEBUG_REMOVE) {
- if (chatty)
- Log.d(TAG, "Unregistered content provider: " + names[j]
- + ", className = " + p.info.name + ", isSyncable = "
- + p.info.isSyncable);
- }
- }
- }
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(p.info.name);
- }
- }
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Providers: " + r);
+ void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) {
+ int N = pkg.providers.size();
+ StringBuilder r = null;
+ int i;
+ for (i=0; i<N; i++) {
+ PackageParser.Provider p = pkg.providers.get(i);
+ mProvidersByComponent.remove(new ComponentName(p.info.packageName,
+ p.info.name));
+ if (p.info.authority == null) {
+
+ /* There was another ContentProvider with this authority when
+ * this app was installed so this authority is null,
+ * Ignore it as we don't have to unregister the provider.
+ */
+ continue;
}
-
- N = pkg.services.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Service s = pkg.services.get(i);
- mServices.removeService(s);
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
+ String names[] = p.info.authority.split(";");
+ for (int j = 0; j < names.length; j++) {
+ if (mProviders.get(names[j]) == p) {
+ mProviders.remove(names[j]);
+ if (DEBUG_REMOVE) {
+ if (chatty)
+ Log.d(TAG, "Unregistered content provider: " + names[j]
+ + ", className = " + p.info.name + ", isSyncable = "
+ + p.info.isSyncable);
}
- r.append(s.info.name);
}
}
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Services: " + r);
+ if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(p.info.name);
}
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Providers: " + r);
+ }
- N = pkg.receivers.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Activity a = pkg.receivers.get(i);
- mReceivers.removeActivity(a, "receiver");
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(a.info.name);
+ N = pkg.services.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ PackageParser.Service s = pkg.services.get(i);
+ mServices.removeService(s);
+ if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
}
+ r.append(s.info.name);
}
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Receivers: " + r);
- }
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Services: " + r);
+ }
- N = pkg.activities.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Activity a = pkg.activities.get(i);
- mActivities.removeActivity(a, "activity");
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(a.info.name);
+ N = pkg.receivers.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ PackageParser.Activity a = pkg.receivers.get(i);
+ mReceivers.removeActivity(a, "receiver");
+ if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
}
+ r.append(a.info.name);
}
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r);
- }
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Receivers: " + r);
+ }
- N = pkg.permissions.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Permission p = pkg.permissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(p.info.name);
- if (bp == null) {
- bp = mSettings.mPermissionTrees.get(p.info.name);
- }
- if (bp != null && bp.perm == p) {
- bp.perm = null;
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(p.info.name);
- }
+ N = pkg.activities.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ PackageParser.Activity a = pkg.activities.get(i);
+ mActivities.removeActivity(a, "activity");
+ if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
}
+ r.append(a.info.name);
}
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
- }
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r);
+ }
- N = pkg.instrumentation.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Instrumentation a = pkg.instrumentation.get(i);
- mInstrumentation.remove(a.getComponentName());
+ N = pkg.permissions.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ PackageParser.Permission p = pkg.permissions.get(i);
+ BasePermission bp = mSettings.mPermissions.get(p.info.name);
+ if (bp == null) {
+ bp = mSettings.mPermissionTrees.get(p.info.name);
+ }
+ if (bp != null && bp.perm == p) {
+ bp.perm = null;
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
- r.append(a.info.name);
+ r.append(p.info.name);
}
}
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r);
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ }
+
+ N = pkg.instrumentation.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ PackageParser.Instrumentation a = pkg.instrumentation.get(i);
+ mInstrumentation.remove(a.getComponentName());
+ if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(a.info.name);
}
}
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r);
+ }
}
private static final boolean isPackageFilename(String name) {
@@ -4716,14 +4965,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 +5043,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 +5057,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 +5081,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 +5167,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 +5240,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 +5254,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 +5279,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 +5380,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 +5399,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 +5428,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 +5438,66 @@ 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;
+ mSettings.mPackagesToBeCleaned.remove(userId);
+ }
+ // 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) {
}
}
@@ -5195,9 +5512,11 @@ public class PackageManagerService extends IPackageManager.Stub {
public void onEvent(int event, String path) {
String removedPackage = null;
- int removedUid = -1;
+ int removedAppId = -1;
+ int[] removedUsers = null;
String addedPackage = null;
- int addedUid = -1;
+ int addedAppId = -1;
+ int[] addedUsers = null;
// TODO post a message to the handler to obtain serial ordering
synchronized (mInstallLock) {
@@ -5223,15 +5542,25 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
PackageParser.Package p = null;
+ PackageSetting ps = null;
// reader
synchronized (mPackages) {
p = mAppDirs.get(fullPathStr);
+ if (p != null) {
+ ps = mSettings.mPackages.get(p.applicationInfo.packageName);
+ if (ps != null) {
+ removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
+ } else {
+ removedUsers = sUserManager.getUserIds();
+ }
+ }
+ addedUsers = sUserManager.getUserIds();
}
if ((event&REMOVE_EVENTS) != 0) {
- if (p != null) {
- removePackageLI(p, true);
- removedPackage = p.applicationInfo.packageName;
- removedUid = p.applicationInfo.uid;
+ if (ps != null) {
+ removePackageLI(ps, true);
+ removedPackage = ps.name;
+ removedAppId = ps.appId;
}
}
@@ -5243,7 +5572,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(), UserHandle.ALL);
if (p != null) {
/*
* TODO this seems dangerous as the package may have
@@ -5256,7 +5585,7 @@ public class PackageManagerService extends IPackageManager.Stub {
p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);
}
addedPackage = p.applicationInfo.packageName;
- addedUid = p.applicationInfo.uid;
+ addedAppId = UserHandle.getAppId(p.applicationInfo.uid);
}
}
}
@@ -5269,16 +5598,16 @@ public class PackageManagerService extends IPackageManager.Stub {
if (removedPackage != null) {
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, removedUid);
+ extras.putInt(Intent.EXTRA_UID, removedAppId);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, removedUsers);
}
if (addedPackage != null) {
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, addedUid);
+ extras.putInt(Intent.EXTRA_UID, addedAppId);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, addedUsers);
}
}
@@ -5304,9 +5633,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 = new UserHandle(UserHandle.getUserId(uid));
+ }
final int filteredFlags;
@@ -5319,14 +5664,61 @@ public class PackageManagerService extends IPackageManager.Stub {
filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;
}
+ verificationParams.setInstallerUid(uid);
+
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 {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can verify applications");
+
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
final PackageVerificationResponse response = new PackageVerificationResponse(
verificationCode, Binder.getCallingUid());
@@ -5335,6 +5727,49 @@ public class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
+ @Override
+ public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+ long millisecondsToDelay) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can extend verification timeouts");
+
+ final PackageVerificationState state = mPendingVerification.get(id);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, Binder.getCallingUid());
+
+ if (millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
+ millisecondsToDelay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
+ }
+ if (millisecondsToDelay < 0) {
+ millisecondsToDelay = 0;
+ }
+ if ((verificationCodeAtTimeout != PackageManager.VERIFICATION_ALLOW)
+ && (verificationCodeAtTimeout != PackageManager.VERIFICATION_REJECT)) {
+ verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
+ }
+
+ 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 void broadcastPackageVerified(int verificationId, Uri packageUri,
+ int verificationCode) {
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
+ intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
+
+ mContext.sendBroadcast(intent, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
+ }
+
private ComponentName matchComponentForVerifier(String packageName,
List<ResolveInfo> receivers) {
ActivityInfo targetReceiver = null;
@@ -5447,20 +5882,45 @@ public class PackageManagerService extends IPackageManager.Stub {
* @return verification timeout in milliseconds
*/
private long getVerificationTimeout() {
- return android.provider.Settings.Secure.getLong(mContext.getContentResolver(),
- android.provider.Settings.Secure.PACKAGE_VERIFIER_TIMEOUT,
+ return android.provider.Settings.Global.getLong(mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
DEFAULT_VERIFICATION_TIMEOUT);
}
/**
+ * Get the default verification agent response code.
+ *
+ * @return default verification response code
+ */
+ private int getDefaultVerificationResponse() {
+ return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
+ DEFAULT_VERIFICATION_RESPONSE);
+ }
+
+ /**
* Check whether or not package verification has been enabled.
*
* @return true if verification should be performed
*/
private boolean isVerificationEnabled() {
+ if (!DEFAULT_VERIFY_ENABLE) {
+ return false;
+ }
+
+ return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
+ }
+
+ /**
+ * 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.PACKAGE_VERIFIER_ENABLE,
- DEFAULT_VERIFY_ENABLE ? 1 : 0) == 1 ? true : false;
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
+ -1);
}
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
@@ -5615,6 +6075,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 +6127,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 +6135,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;
@@ -5671,19 +6143,20 @@ public class PackageManagerService extends IPackageManager.Stub {
mounted = true;
} else {
final String status = Environment.getExternalStorageState();
-
- mounted = status.equals(Environment.MEDIA_MOUNTED)
- || status.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
+ mounted = (Environment.MEDIA_MOUNTED.equals(status)
+ || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status));
}
if (mounted) {
- final File externalCacheDir = Environment
+ final UserEnvironment userEnv = new UserEnvironment(mStats.userHandle);
+
+ final File externalCacheDir = userEnv
.getExternalStorageAppCacheDirectory(mStats.packageName);
final long externalCacheSize = mContainerService
.calculateDirectorySize(externalCacheDir.getPath());
mStats.externalCacheSize = externalCacheSize;
- final File externalDataDir = Environment
+ final File externalDataDir = userEnv
.getExternalStorageAppDataDirectory(mStats.packageName);
long externalDataSize = mContainerService.calculateDirectorySize(externalDataDir
.getPath());
@@ -5693,12 +6166,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mStats.externalDataSize = externalDataSize;
- final File externalMediaDir = Environment
+ final File externalMediaDir = userEnv
.getExternalStorageAppMediaDirectory(mStats.packageName);
mStats.externalMediaSize = mContainerService
.calculateDirectorySize(externalMediaDir.getPath());
- final File externalObbDir = Environment
+ final File externalObbDir = userEnv
.getExternalStorageAppObbDirectory(mStats.packageName);
mStats.externalObbSize = mContainerService.calculateDirectorySize(externalObbDir
.getPath());
@@ -5729,8 +6202,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 +6210,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 +6237,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) {
@@ -5850,7 +6339,8 @@ public class PackageManagerService extends IPackageManager.Stub {
packageFile = mTempPackage;
FileUtils.setPermissions(packageFile.getAbsolutePath(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IROTH,
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP
+ | FileUtils.S_IROTH,
-1, -1);
} else {
packageFile = null;
@@ -5884,6 +6374,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 +6411,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 +6430,29 @@ public class PackageManagerService extends IPackageManager.Stub {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, flags);
- if (verificationURI != null) {
- verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI,
- verificationURI);
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME,
+ pkgLite.packageName);
+
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
+ pkgLite.versionCode);
+
+ 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());
+ }
+ if (verificationParams.getInstallerUid() >= 0) {
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
+ verificationParams.getInstallerUid());
+ }
}
final PackageVerificationState verificationState = new PackageVerificationState(
@@ -6018,12 +6531,12 @@ public class PackageManagerService extends IPackageManager.Stub {
// will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
- }
- if (mTempPackage != null) {
- if (!mTempPackage.delete()) {
- Slog.w(TAG, "Couldn't delete temporary file: "
- + mTempPackage.getAbsolutePath());
+ if (mTempPackage != null) {
+ if (!mTempPackage.delete()) {
+ Slog.w(TAG, "Couldn't delete temporary file: " +
+ mTempPackage.getAbsolutePath());
+ }
}
}
}
@@ -6064,7 +6577,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 +6731,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 +6792,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,12 +6806,12 @@ 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();
resourceFileName = getResourcePathFromCodePath();
- libraryPath = new File(dataDir, LIB_DIR_NAME).getPath();
+ libraryPath = new File(mAppLibInstallDir, pkgName).getPath();
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
@@ -6330,6 +6848,7 @@ public class PackageManagerService extends IPackageManager.Stub {
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
codeFileName = createTempPackageFile(installDir).getPath();
resourceFileName = getResourcePathFromCodePath();
+ libraryPath = getLibraryPathFromCodePath();
created = true;
}
@@ -6384,6 +6903,23 @@ public class PackageManagerService extends IPackageManager.Stub {
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}
+
+ final File nativeLibraryFile = new File(getNativeLibraryPath());
+ Slog.i(TAG, "Copying native libraries to " + nativeLibraryFile.getPath());
+ if (nativeLibraryFile.exists()) {
+ NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
+ nativeLibraryFile.delete();
+ }
+ try {
+ int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);
+ if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+ return copyRet;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Copying native libraries failed", e);
+ ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ }
+
return ret;
}
@@ -6401,6 +6937,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
final File oldCodeFile = new File(getCodePath());
final File oldResourceFile = new File(getResourcePath());
+ final File oldLibraryFile = new File(getNativeLibraryPath());
// Rename APK file based on packageName
final String apkName = getNextCodePath(oldCodePath, pkgName, ".apk");
@@ -6415,7 +6952,20 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isFwdLocked() && !oldResourceFile.renameTo(newResFile)) {
return false;
}
- resourceFileName = getResourcePathFromCodePath();
+ resourceFileName = newResFile.getPath();
+
+ // Rename library path
+ final File newLibraryFile = new File(getLibraryPathFromCodePath());
+ if (newLibraryFile.exists()) {
+ NativeLibraryHelper.removeNativeBinariesFromDirLI(newLibraryFile);
+ newLibraryFile.delete();
+ }
+ if (!oldLibraryFile.renameTo(newLibraryFile)) {
+ Slog.e(TAG, "Cannot rename native library directory "
+ + oldLibraryFile.getPath() + " to " + newLibraryFile.getPath());
+ return false;
+ }
+ libraryPath = newLibraryFile.getPath();
// Attempt to set permissions
if (!setPermissions()) {
@@ -6466,8 +7016,15 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ private String getLibraryPathFromCodePath() {
+ return new File(mAppLibInstallDir, getApkName(getCodePath())).getPath();
+ }
+
@Override
String getNativeLibraryPath() {
+ if (libraryPath == null) {
+ libraryPath = getLibraryPathFromCodePath();
+ }
return libraryPath;
}
@@ -6493,6 +7050,15 @@ public class PackageManagerService extends IPackageManager.Stub {
publicSourceFile.delete();
}
}
+
+ if (libraryPath != null) {
+ File nativeLibraryFile = new File(libraryPath);
+ NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
+ if (!nativeLibraryFile.delete()) {
+ Slog.w(TAG, "Couldn't delete native library directory " + libraryPath);
+ }
+ }
+
return ret;
}
@@ -6562,13 +7128,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 +7147,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;
}
@@ -6735,7 +7305,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final int groupOwner;
final String protectedFile;
if (isFwdLocked()) {
- groupOwner = uid;
+ groupOwner = UserHandle.getSharedAppGid(uid);
protectedFile = RES_FILE_NAME;
} else {
groupOwner = -1;
@@ -6817,7 +7387,8 @@ public class PackageManagerService extends IPackageManager.Stub {
int doPostCopy(int uid) {
if (isFwdLocked()) {
if (uid < Process.FIRST_APPLICATION_UID
- || !PackageHelper.fixSdPermissions(cid, uid, RES_FILE_NAME)) {
+ || !PackageHelper.fixSdPermissions(cid, UserHandle.getSharedAppGid(uid),
+ RES_FILE_NAME)) {
Slog.e(TAG, "Failed to finalize " + cid);
PackageHelper.destroySdDir(cid);
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
@@ -6910,6 +7481,10 @@ public class PackageManagerService extends IPackageManager.Stub {
class PackageInstalledInfo {
String name;
int uid;
+ // The set of users that originally had this package installed.
+ int[] origUsers;
+ // The set of users that now have this package installed.
+ int[] newUsers;
PackageParser.Package pkg;
int returnCode;
PackageRemovedInfo removedInfo;
@@ -6919,14 +7494,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 +7522,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 +7539,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 +7564,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 +7588,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 +7597,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 +7618,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 +7634,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, null) == null) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
return;
}
@@ -7080,8 +7652,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;
@@ -7111,7 +7682,7 @@ public class PackageManagerService extends IPackageManager.Stub {
res.removedInfo.uid = oldPkg.applicationInfo.uid;
res.removedInfo.removedPackage = packageName;
// Remove existing system package
- removePackageLI(oldPkg, true);
+ removePackageLI(oldPkgSetting, true);
// writer
synchronized (mPackages) {
if (!mSettings.disableSystemPackageLPw(packageName) && deletedPackage != null) {
@@ -7130,7 +7701,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) {
@@ -7150,10 +7721,10 @@ public class PackageManagerService extends IPackageManager.Stub {
// Re installation failed. Restore old information
// Remove new pkg information
if (newPackage != null) {
- removePackageLI(newPackage, true);
+ removeInstalledPackageLI(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) {
@@ -7309,6 +7880,7 @@ public class PackageManagerService extends IPackageManager.Stub {
systemApp = (ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
}
@@ -7327,11 +7899,17 @@ 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,
- installerPackageName,res);
+ installNewPackageLI(pkg, parseFlags, scanMode, args.user,
+ installerPackageName, res);
+ }
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
+ }
}
}
@@ -7380,17 +7958,23 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private void deleteTempPackageFiles() {
- FilenameFilter filter = new FilenameFilter() {
+ final FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith("vmdl") && name.endsWith(".tmp");
}
};
- String tmpFilesList[] = mAppInstallDir.list(filter);
- if(tmpFilesList == null) {
+ deleteTempPackageFilesInDirectory(mAppInstallDir, filter);
+ deleteTempPackageFilesInDirectory(mDrmAppPrivateInstallDir, filter);
+ }
+
+ private static final void deleteTempPackageFilesInDirectory(File directory,
+ FilenameFilter filter) {
+ final String[] tmpFilesList = directory.list(filter);
+ if (tmpFilesList == null) {
return;
}
- for(int i = 0; i < tmpFilesList.length; i++) {
- File tmpFile = new File(mAppInstallDir, tmpFilesList[i]);
+ for (int i = 0; i < tmpFilesList.length; i++) {
+ final File tmpFile = new File(directory, tmpFilesList[i]);
tmpFile.delete();
}
}
@@ -7423,10 +8007,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 +8037,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 +8052,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 +8084,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 +8094,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);
}
}
}
@@ -7540,37 +8128,32 @@ public class PackageManagerService extends IPackageManager.Stub {
* make sure this flag is set for partially installed apps. If not its meaningless to
* delete a partially installed application.
*/
- private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo,
+ private void removePackageDataLI(PackageSetting ps, PackageRemovedInfo outInfo,
int flags, boolean writeSettings) {
- String packageName = p.packageName;
- if (outInfo != null) {
- outInfo.removedPackage = packageName;
- }
- removePackageLI(p, (flags&REMOVE_CHATTY) != 0);
+ String packageName = ps.name;
+ removePackageLI(ps, (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.queryInstalledUsers(sUserManager.getUserIds(), true)
+ : 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 +8162,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
}
}
- clearPackagePreferredActivitiesLPw(deletedPs.name);
+ clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
}
}
// can downgrade to reader
@@ -7593,38 +8176,32 @@ public class PackageManagerService extends IPackageManager.Stub {
/*
* Tries to delete system package.
*/
- private boolean deleteSystemPackageLI(PackageParser.Package p,
+ private boolean deleteSystemPackageLI(PackageSetting newPs,
int flags, PackageRemovedInfo outInfo, boolean writeSettings) {
- ApplicationInfo applicationInfo = p.applicationInfo;
- //applicable for non-partially installed applications only
- if (applicationInfo == null) {
- Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
- return false;
- }
- PackageSetting ps = null;
+ PackageSetting disabledPs = null;
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
// reader
synchronized (mPackages) {
- ps = mSettings.getDisabledSystemPkgLPr(p.packageName);
+ disabledPs = mSettings.getDisabledSystemPkgLPr(newPs.name);
}
- if (ps == null) {
- Slog.w(TAG, "Attempt to delete unknown system package "+ p.packageName);
+ if (disabledPs == null) {
+ Slog.w(TAG, "Attempt to delete unknown system package "+ newPs.name);
return false;
} else {
Log.i(TAG, "Deleting system pkg from data partition");
}
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
- if (ps.versionCode < p.mVersionCode) {
+ if (disabledPs.versionCode < newPs.versionCode) {
// 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,
+ boolean ret = deleteInstalledPackageLI(newPs, true, flags, outInfo,
writeSettings);
if (!ret) {
return false;
@@ -7632,17 +8209,18 @@ public class PackageManagerService extends IPackageManager.Stub {
// writer
synchronized (mPackages) {
// Reinstate the old system package
- mSettings.enableSystemPackageLPw(p.packageName);
+ mSettings.enableSystemPackageLPw(newPs.name);
// Remove any native libraries from the upgraded package.
- NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir);
+ NativeLibraryHelper.removeNativeBinariesLI(newPs.nativeLibraryPathString);
}
// Install the system package
- PackageParser.Package newPkg = scanPackageLI(ps.codePath,
+ PackageParser.Package newPkg = scanPackageLI(disabledPs.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);
+ Slog.w(TAG, "Failed to restore system package:" + newPs.name
+ + " with error:" + mLastScanError);
return false;
}
// writer
@@ -7657,28 +8235,20 @@ public class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private boolean deleteInstalledPackageLI(PackageParser.Package p,
+ private boolean deleteInstalledPackageLI(PackageSetting ps,
boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo,
boolean writeSettings) {
- ApplicationInfo applicationInfo = p.applicationInfo;
- if (applicationInfo == null) {
- Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
- return false;
- }
if (outInfo != null) {
- outInfo.uid = applicationInfo.uid;
+ outInfo.uid = ps.appId;
}
// Delete package data from internal structures and also remove data if flag is set
- removePackageDataLI(p, outInfo, flags, writeSettings);
+ removePackageDataLI(ps, outInfo, flags, writeSettings);
// Delete application code and resources
if (deleteCodeAndResources) {
- // TODO can pick up from PackageSettings as well
- int installFlags = isExternal(p) ? PackageManager.INSTALL_EXTERNAL : 0;
- installFlags |= isForwardLocked(p) ? PackageManager.INSTALL_FORWARD_LOCK : 0;
- outInfo.args = createInstallArgs(installFlags, applicationInfo.sourceDir,
- applicationInfo.publicSourceDir, applicationInfo.nativeLibraryDir);
+ outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,
+ ps.resourcePathString, ps.nativeLibraryPathString);
}
return true;
}
@@ -7686,54 +8256,78 @@ 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) {
Slog.w(TAG, "Attempt to delete null packageName.");
return false;
}
- PackageParser.Package p;
+ PackageSetting ps;
boolean dataOnly = false;
+ int removeUser = -1;
+ int appId = -1;
synchronized (mPackages) {
- p = mPackages.get(packageName);
- if (p == null) {
- //this retrieves partially installed apps
- dataOnly = true;
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
- return false;
+ ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
+ return false;
+ }
+ if (!isSystemApp(ps) && 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());
}
- p = ps.pkg;
}
}
- 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) {
// Delete application data first
- removePackageDataLI(p, outInfo, flags, writeSettings);
+ removePackageDataLI(ps, outInfo, flags, writeSettings);
return true;
}
- // At this point the package should have ApplicationInfo associated with it
- if (p.applicationInfo == null) {
- Slog.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
- return false;
- }
boolean ret = false;
- if (isSystemApp(p)) {
- Log.i(TAG, "Removing system package:"+p.packageName);
+ if (isSystemApp(ps)) {
+ Log.i(TAG, "Removing system package:" + ps.name);
// When an updated system application is deleted we delete the existing resources as well and
// fall back to existing code in system partition
- ret = deleteSystemPackageLI(p, flags, outInfo, writeSettings);
+ ret = deleteSystemPackageLI(ps, flags, outInfo, writeSettings);
} else {
- Log.i(TAG, "Removing non-system package:"+p.packageName);
+ Log.i(TAG, "Removing non-system package:" + ps.name);
// Kill application pre-emptively especially for apps on sd.
- killApplication(packageName, p.applicationInfo.uid);
- ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo,
+ killApplication(packageName, ps.appId);
+ ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, outInfo,
writeSettings);
}
return ret;
@@ -7755,7 +8349,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 +8365,54 @@ 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 UserEnvironment userEnv = new UserEnvironment(curUser);
+ final File externalCacheDir = userEnv
+ .getExternalStorageAppCacheDirectory(packageName);
try {
- conn.mContainerService.clearDirectory(externalMediaDir.toString());
+ conn.mContainerService.clearDirectory(externalCacheDir.toString());
} catch (RemoteException e) {
}
+ if (allData) {
+ final File externalDataDir = userEnv
+ .getExternalStorageAppDataDirectory(packageName);
+ try {
+ conn.mContainerService.clearDirectory(externalDataDir.toString());
+ } catch (RemoteException e) {
+ }
+ final File externalMediaDir = userEnv
+ .getExternalStorageAppMediaDirectory(packageName);
+ try {
+ conn.mContainerService.clearDirectory(externalMediaDir.toString());
+ } catch (RemoteException e) {
+ }
+ }
+ } finally {
+ mContext.unbindService(conn);
}
- } finally {
- mContext.unbindService(conn);
}
}
}
@@ -7818,7 +8422,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final IPackageDataObserver observer, final int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
- checkValidCaller(Binder.getCallingUid(), userId);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "clear application data");
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
@@ -7827,7 +8431,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 +8497,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 +8505,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 +8545,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 +8561,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 +8599,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 +8651,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();
+ enforceCrossUserPermission(callingUid, userId, true, "add preferred activity");
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 +8708,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 +8732,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.mPreferredActivities.removeFilter(pa);
}
}
- addPreferredActivity(filter, match, set, activity);
+ addPreferredActivity(filter, match, set, activity, callingUserId);
}
}
@@ -8147,17 +8756,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 +8792,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) {
@@ -8227,7 +8844,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, false, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
boolean isApp = (className == null);
@@ -8248,7 +8865,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 +8914,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 +8963,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) {
@@ -8355,7 +8972,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, true, "stop package");
// writer
synchronized (mPackages) {
if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission,
@@ -8376,7 +8993,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, false, "get enabled");
// reader
synchronized (mPackages) {
return mSettings.getApplicationEnabledSettingLPr(packageName, userId);
@@ -8387,7 +9004,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public int getComponentEnabledSetting(ComponentName componentName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, false, "get component enabled");
// reader
synchronized (mPackages) {
return mSettings.getComponentEnabledSettingLPr(componentName, userId);
@@ -8951,7 +9568,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 +9589,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 +9634,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 +9743,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 {
@@ -9151,7 +9768,8 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky) throws RemoteException {
+ Bundle extras, boolean ordered, boolean sticky,
+ int sendingUser) throws RemoteException {
Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
reportStatus ? 1 : 0, 1, keys);
mHandler.sendMessage(msg);
@@ -9164,9 +9782,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 +9838,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);
}
@@ -9288,31 +9910,26 @@ public class PackageManagerService extends IPackageManager.Stub {
final String newNativePath = mp.targetArgs
.getNativeLibraryPath();
- try {
- final File newNativeDir = new File(newNativePath);
+ final File newNativeDir = new File(newNativePath);
- final String libParentDir = newNativeDir.getParentFile()
- .getCanonicalPath();
- if (newNativeDir.getParentFile().getCanonicalPath()
- .equals(pkg.applicationInfo.dataDir)) {
- if (mInstaller
- .unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) {
+ if (!isForwardLocked(pkg) && !isExternal(pkg)) {
+ synchronized (mInstallLock) {
+ if (mInstaller.linkNativeLibraryDirectory(
+ pkg.applicationInfo.dataDir, newNativePath) < 0) {
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
- } else {
- NativeLibraryHelper.copyNativeBinariesIfNeededLI(
- new File(newCodePath), newNativeDir);
}
- } else {
+ }
+ NativeLibraryHelper.copyNativeBinariesIfNeededLI(new File(
+ newCodePath), newNativeDir);
+ } else {
+ synchronized (mInstallLock) {
if (mInstaller.linkNativeLibraryDirectory(
pkg.applicationInfo.dataDir, newNativePath) < 0) {
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
}
}
- } catch (IOException e) {
- returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
}
-
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
pkg.mPath = newCodePath;
// Move dex files around
@@ -9415,48 +10032,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 +10078,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..d8f7345 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[] queryInstalledUsers(int[] users, boolean installed) {
+ int num = 0;
+ for (int user : users) {
+ if (getInstalled(user) == installed) {
+ num++;
+ }
+ }
+ int[] res = new int[num];
+ num = 0;
+ for (int user : users) {
+ if (getInstalled(user) == installed) {
+ 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..b075da3 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -32,23 +32,24 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
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 +80,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 +92,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 +126,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 +159,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 +179,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 +204,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 +369,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 +413,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 +446,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 +485,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 +509,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;
}
@@ -527,6 +566,10 @@ final class Settings {
if (p.signatures.mSignatures == null) {
p.signatures.assignSignatures(pkg.mSignatures);
}
+ // Update flags if needed.
+ if (pkg.applicationInfo.flags != p.pkgFlags) {
+ p.pkgFlags = pkg.applicationInfo.flags;
+ }
// If this app defines a shared user id initialize
// the shared user signatures as well.
if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
@@ -704,13 +747,12 @@ final class Settings {
}
private File getUserPackagesStateFile(int userId) {
- return new File(mSystemDir,
- "users/" + userId + "/package-restrictions.xml");
+ return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
}
private File getUserPackagesStateBackupFile(int userId) {
- return new File(mSystemDir,
- "users/" + userId + "/package-restrictions-backup.xml");
+ return new File(Environment.getUserSystemDirectory(userId),
+ "package-restrictions-backup.xml");
}
void writeAllUsersPackageRestrictionsLPr() {
@@ -735,6 +777,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 +811,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 +860,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 +886,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 +918,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 +933,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 +944,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 +981,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 +1258,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(j);
+ 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 +1524,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 +1612,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 +1676,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 +1703,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 +1738,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 +2373,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 +2387,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 +2440,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 +2465,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 +2477,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 +2514,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",
};
@@ -2505,8 +2605,8 @@ final class Settings {
first = false;
pw.print("anyDensity");
}
+ pw.println("]");
}
- pw.println("]");
pw.print(" timeStamp=");
date.setTime(ps.timeStamp);
pw.println(sdf.format(date));
@@ -2520,25 +2620,31 @@ final class Settings {
pw.print(" installerPackageName="); pw.println(ps.installerPackageName);
}
pw.print(" signatures="); pw.println(ps.signatures);
- 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(" permissionsFixed="); pw.println(ps.permissionsFixed);
+ pw.print(" haveGids="); pw.println(ps.haveGids);
+ pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
+ 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..be86628
--- /dev/null
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -0,0 +1,723 @@
+/*
+ * 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.app.ActivityManagerNative;
+import android.app.IStopUserCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+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.RemoteException;
+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 final int mUserLimit;
+
+ 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;
+ mUserLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_multiuserMaximumUsers);
+ 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);
+ }
+ }
+ sendUserInfoChangedBroadcast(userId);
+ }
+
+ @Override
+ public void setUserIcon(int userId, Bitmap bitmap) {
+ checkManageUsersPermission("update users");
+ synchronized (mPackagesLock) {
+ UserInfo info = mUsers.get(userId);
+ if (info == null) return;
+ writeBitmapLocked(info, bitmap);
+ writeUserLocked(info);
+ }
+ sendUserInfoChangedBroadcast(userId);
+ }
+
+ private void sendUserInfoChangedBroadcast(int userId) {
+ Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
+ changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId));
+ }
+
+ @Override
+ public Bitmap getUserIcon(int userId) {
+ checkManageUsersPermission("read users");
+ synchronized (mPackagesLock) {
+ UserInfo info = mUsers.get(userId);
+ if (info == null || info.iconPath == null) return null;
+ return BitmapFactory.decodeFile(info.iconPath);
+ }
+ }
+
+ @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:
+ }
+
+ public void makeInitialized(int userId) {
+ checkManageUsersPermission("makeInitialized");
+ synchronized (mPackagesLock) {
+ UserInfo info = mUsers.get(userId);
+ if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
+ info.flags |= UserInfo.FLAG_INITIALIZED;
+ writeUserLocked(info);
+ }
+ }
+ }
+
+ /**
+ * Check if we've hit the limit of how many users can be created.
+ */
+ private boolean isUserLimitReachedLocked() {
+ int nUsers = mUsers.size();
+ return nUsers >= mUserLimit;
+ }
+
+ /**
+ * 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 void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
+ 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);
+ }
+ FileOutputStream os;
+ if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) {
+ info.iconPath = file.getAbsolutePath();
+ }
+ try {
+ os.close();
+ } catch (IOException ioe) {
+ // What the ... !
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(LOG_TAG, "Error setting photo for user ", e);
+ }
+ }
+
+ /**
+ * 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.
+ */
+ public 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");
+
+ final long ident = Binder.clearCallingIdentity();
+ final UserInfo userInfo;
+ try {
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
+ if (isUserLimitReachedLocked()) return null;
+ int userId = getNextAvailableIdLocked();
+ userInfo = new UserInfo(userId, name, null, flags);
+ File userPath = new File(mBaseUserPath, Integer.toString(userId));
+ 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.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+ android.Manifest.permission.MANAGE_USERS);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ 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");
+ final UserInfo user;
+ synchronized (mPackagesLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
+ }
+
+ int res;
+ try {
+ res = ActivityManagerNative.getDefault().stopUser(userHandle,
+ new IStopUserCallback.Stub() {
+ @Override
+ public void userStopped(int userId) {
+ finishRemoveUser(userId);
+ }
+ @Override
+ public void userStopAborted(int userId) {
+ }
+ });
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ return res == ActivityManager.USER_OP_SUCCESS;
+ }
+
+ void finishRemoveUser(int userHandle) {
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
+ // 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();
+ removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
+ }
+ }
+
+ // Let other services shutdown any activity
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
+ mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+ android.Manifest.permission.MANAGE_USERS);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void removeDirectoryRecursive(File parent) {
+ if (parent.isDirectory()) {
+ String[] files = parent.list();
+ for (String filename : files) {
+ File child = new File(parent, filename);
+ removeDirectoryRecursive(child);
+ }
+ }
+ parent.delete();
+ }
+
+ @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 getNextAvailableIdLocked() {
+ synchronized (mPackagesLock) {
+ int i = 10;
+ 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..5f4a786
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -0,0 +1,1160 @@
+/*
+ * 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.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 =
+ PowerManager.useScreenAutoBrightnessAdjustmentFeature();
+
+ // 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 display manager.
+ private final DisplayManager mDisplayManager;
+
+ // 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());
+ mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+
+ 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 = mDisplayManager.getDisplay(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(final 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);
+
+ mHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ dumpLocal(pw);
+ }
+ }, 1000);
+ }
+
+ 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..1bd7811
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -0,0 +1,276 @@
+/*
+ * 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) {
+ mScreenBrightnessModulator.setBrightness(0, true /*sync*/);
+ PowerManagerService.nativeSetScreenState(false);
+ }
+
+ if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
+ mElectronBeam.draw(mElectronBeamLevel);
+ }
+
+ if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
+ PowerManagerService.nativeSetScreenState(true);
+ }
+
+ if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0
+ && mScreenOn) {
+ mScreenBrightnessModulator.setBrightness(
+ (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/);
+ }
+
+ 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..0c68997
--- /dev/null
+++ b/services/java/com/android/server/power/ElectronBeam.java
@@ -0,0 +1,653 @@
+/*
+ * 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,
+ "ElectronBeam", mDisplayWidth, mDisplayHeight,
+ PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN);
+ } catch (Surface.OutOfResourcesException ex) {
+ Slog.e(TAG, "Unable to create surface.", ex);
+ return false;
+ }
+ }
+
+ mSurface.setLayerStack(mDisplayLayerStack);
+ 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..ce1e147
--- /dev/null
+++ b/services/java/com/android/server/power/Notifier.java
@@ -0,0 +1,442 @@
+/*
+ * 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.UserHandle;
+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.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, 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.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, 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..c9b5d90
--- /dev/null
+++ b/services/java/com/android/server/power/PhotonicModulator.java
@@ -0,0 +1,98 @@
+/*
+ * 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;
+ }
+
+ /**
+ * Sets the backlight brightness, synchronously or asynchronously.
+ *
+ * @param lightValue The new light value, from 0 to 255.
+ * @param sync If true, waits for the brightness change to complete before returning.
+ */
+ public void setBrightness(int lightValue, boolean sync) {
+ synchronized (mLock) {
+ if (lightValue != mPendingLightValue) {
+ mPendingLightValue = lightValue;
+ if (!mPendingChange) {
+ mPendingChange = true;
+ mSuspendBlocker.acquire();
+ mExecutor.execute(mTask);
+ }
+ }
+ if (sync) {
+ while (mPendingChange) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException ex) {
+ // ignore it
+ }
+ }
+ }
+ }
+ }
+
+ 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;
+ mLock.notifyAll();
+ 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..fda619c
--- /dev/null
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -0,0 +1,2122 @@
+/*
+ * 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.Global.getUriFor(
+ Settings.Global.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.Global.getInt(resolver,
+ Settings.Global.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
+ | WAKE_LOCK_PROXIMITY_SCREEN_OFF)) == 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.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.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..c7f7390 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;
@@ -37,13 +37,13 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.os.SystemVibrator;
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;
@@ -297,8 +297,8 @@ public final class ShutdownThread extends Thread {
// First send the high-level shut down broadcast.
mActionDone = false;
- mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
- br, mHandler, 0, null, null);
+ mContext.sendOrderedBroadcastAsUser(new Intent(Intent.ACTION_SHUTDOWN),
+ UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
@@ -385,9 +385,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 +402,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 +428,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/updatable/CertPinInstallReceiver.java b/services/java/com/android/server/updatable/CertPinInstallReceiver.java
new file mode 100644
index 0000000..c03fbc3
--- /dev/null
+++ b/services/java/com/android/server/updatable/CertPinInstallReceiver.java
@@ -0,0 +1,24 @@
+/*
+ * 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.updates;
+
+public class CertPinInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public CertPinInstallReceiver() {
+ super("/data/misc/keychain/", "pins", "metadata/", "version");
+ }
+}
diff --git a/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java
new file mode 100644
index 0000000..a74a648
--- /dev/null
+++ b/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java
@@ -0,0 +1,265 @@
+/*
+ * 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.updates;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.os.FileUtils;
+import android.util.Base64;
+import android.util.EventLog;
+import android.util.Slog;
+
+import com.android.server.EventLogTags;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+
+import libcore.io.IoUtils;
+
+public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "ConfigUpdateInstallReceiver";
+
+ private static final String EXTRA_CONTENT_PATH = "CONTENT_PATH";
+ private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH";
+ private static final String EXTRA_SIGNATURE = "SIGNATURE";
+ private static final String EXTRA_VERSION_NUMBER = "VERSION";
+
+ private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate";
+
+ private final File updateDir;
+ private final File updateContent;
+ private final File updateVersion;
+
+ public ConfigUpdateInstallReceiver(String updateDir, String updateContentPath,
+ String updateMetadataPath, String updateVersionPath) {
+ this.updateDir = new File(updateDir);
+ this.updateContent = new File(updateDir, updateContentPath);
+ File updateMetadataDir = new File(updateDir, updateMetadataPath);
+ this.updateVersion = new File(updateMetadataDir, updateVersionPath);
+ }
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ // get the certificate from Settings.Secure
+ X509Certificate cert = getCert(context.getContentResolver());
+ // get the content path from the extras
+ String altContent = getAltContent(intent);
+ // get the version from the extras
+ int altVersion = getVersionFromIntent(intent);
+ // get the previous value from the extras
+ String altRequiredHash = getRequiredHashFromIntent(intent);
+ // get the signature from the extras
+ String altSig = getSignatureFromIntent(intent);
+ // get the version currently being used
+ int currentVersion = getCurrentVersion();
+ // get the hash of the currently used value
+ String currentHash = getCurrentHash(getCurrentContent());
+ if (!verifyVersion(currentVersion, altVersion)) {
+ EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED,
+ "New version is not greater than current version");
+ } else if (!verifyPreviousHash(currentHash, altRequiredHash)) {
+ EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED,
+ "Current hash did not match required value");
+ } else if (!verifySignature(altContent, altVersion, altRequiredHash, altSig,
+ cert)) {
+ EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED,
+ "Signature did not verify");
+ } else {
+ // install the new content
+ Slog.i(TAG, "Found new update, installing...");
+ install(altContent, altVersion);
+ Slog.i(TAG, "Installation successful");
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not update content!", e);
+ // keep the error message <= 100 chars
+ String errMsg = e.toString();
+ if (errMsg.length() > 100) {
+ errMsg = errMsg.substring(0, 99);
+ }
+ EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, errMsg);
+ }
+ }
+ }.start();
+ }
+
+ private X509Certificate getCert(ContentResolver cr) {
+ // get the cert from settings
+ String cert = Settings.Secure.getString(cr, UPDATE_CERTIFICATE_KEY);
+ // convert it into a real certificate
+ try {
+ byte[] derCert = Base64.decode(cert.getBytes(), Base64.DEFAULT);
+ InputStream istream = new ByteArrayInputStream(derCert);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) cf.generateCertificate(istream);
+ } catch (CertificateException e) {
+ throw new IllegalStateException("Got malformed certificate from settings, ignoring", e);
+ }
+ }
+
+ private String getContentFromIntent(Intent i) {
+ String extraValue = i.getStringExtra(EXTRA_CONTENT_PATH);
+ if (extraValue == null) {
+ throw new IllegalStateException("Missing required content path, ignoring.");
+ }
+ return extraValue;
+ }
+
+ private int getVersionFromIntent(Intent i) throws NumberFormatException {
+ String extraValue = i.getStringExtra(EXTRA_VERSION_NUMBER);
+ if (extraValue == null) {
+ throw new IllegalStateException("Missing required version number, ignoring.");
+ }
+ return Integer.parseInt(extraValue.trim());
+ }
+
+ private String getRequiredHashFromIntent(Intent i) {
+ String extraValue = i.getStringExtra(EXTRA_REQUIRED_HASH);
+ if (extraValue == null) {
+ throw new IllegalStateException("Missing required previous hash, ignoring.");
+ }
+ return extraValue.trim();
+ }
+
+ private String getSignatureFromIntent(Intent i) {
+ String extraValue = i.getStringExtra(EXTRA_SIGNATURE);
+ if (extraValue == null) {
+ throw new IllegalStateException("Missing required signature, ignoring.");
+ }
+ return extraValue.trim();
+ }
+
+ private int getCurrentVersion() throws NumberFormatException {
+ try {
+ String strVersion = IoUtils.readFileAsString(updateVersion.getCanonicalPath()).trim();
+ return Integer.parseInt(strVersion);
+ } catch (IOException e) {
+ Slog.i(TAG, "Couldn't find current metadata, assuming first update", e);
+ return 0;
+ }
+ }
+
+ private String getAltContent(Intent i) throws IOException {
+ String contents = IoUtils.readFileAsString(getContentFromIntent(i));
+ return contents.trim();
+ }
+
+ private String getCurrentContent() {
+ try {
+ return IoUtils.readFileAsString(updateContent.getCanonicalPath()).trim();
+ } catch (IOException e) {
+ Slog.i(TAG, "Failed to read current content, assuming first update!", e);
+ return null;
+ }
+ }
+
+ private static String getCurrentHash(String content) {
+ if (content == null) {
+ return "0";
+ }
+ try {
+ MessageDigest dgst = MessageDigest.getInstance("SHA512");
+ byte[] encoded = content.getBytes();
+ byte[] fingerprint = dgst.digest(encoded);
+ return IntegralToString.bytesToHexString(fingerprint, false);
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private boolean verifyVersion(int current, int alternative) {
+ return (current < alternative);
+ }
+
+ private boolean verifyPreviousHash(String current, String required) {
+ // this is an optional value- if the required field is NONE then we ignore it
+ if (required.equals("NONE")) {
+ return true;
+ }
+ // otherwise, verify that we match correctly
+ return current.equals(required);
+ }
+
+ private boolean verifySignature(String content, int version, String requiredPrevious,
+ String signature, X509Certificate cert) throws Exception {
+ Signature signer = Signature.getInstance("SHA512withRSA");
+ signer.initVerify(cert);
+ signer.update(content.getBytes());
+ signer.update(Long.toString(version).getBytes());
+ signer.update(requiredPrevious.getBytes());
+ return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT));
+ }
+
+ private void writeUpdate(File dir, File file, String content) {
+ FileOutputStream out = null;
+ File tmp = null;
+ try {
+ // create the temporary file
+ tmp = File.createTempFile("journal", "", dir);
+ // create the parents for the destination file
+ File parent = file.getParentFile();
+ parent.mkdirs();
+ // check that they were created correctly
+ if (!parent.exists()) {
+ throw new IOException("Failed to create directory " + parent.getCanonicalPath());
+ }
+ // mark tmp -rw-r--r--
+ tmp.setReadable(true, false);
+ // write to it
+ out = new FileOutputStream(tmp);
+ out.write(content.getBytes());
+ // sync to disk
+ out.getFD().sync();
+ // atomic rename
+ if (!tmp.renameTo(file)) {
+ throw new IOException("Failed to atomically rename " + file.getCanonicalPath());
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write update", e);
+ } finally {
+ if (tmp != null) {
+ tmp.delete();
+ }
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private void install(String content, int version) {
+ writeUpdate(updateDir, updateContent, content);
+ writeUpdate(updateDir, updateVersion, Long.toString(version));
+ }
+}
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..1bb3a2c
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDebuggingManager.java
@@ -0,0 +1,324 @@
+/*
+ * 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 Handler mHandler;
+ private final HandlerThread mHandlerThread;
+ private Thread mThread;
+ private boolean mAdbEnabled = false;
+ private String mFingerprints;
+ private LocalSocket mSocket = null;
+ private OutputStream mOutputStream = null;
+
+ public UsbDebuggingManager(Context context) {
+ 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 = new Thread(UsbDebuggingManager.this);
+ mThread.start();
+
+ break;
+
+ case MESSAGE_ADB_DISABLED:
+ if (!mAdbEnabled)
+ break;
+
+ mAdbEnabled = false;
+ closeSocket();
+
+ try {
+ mThread.join();
+ } catch (Exception ex) {
+ }
+
+ mThread = null;
+ mOutputStream = null;
+ mSocket = null;
+ break;
+
+ 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..3ef6d4c 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,6 +41,7 @@ import android.os.Message;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.os.SystemClock;
@@ -114,6 +115,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 +168,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() {
@@ -177,12 +183,9 @@ public class UsbDeviceManager {
// We do not show the USB notification if the primary volume supports mass storage.
// The legacy mass storage UI will be used instead.
boolean massStorageSupported = false;
- StorageManager storageManager = (StorageManager)
- mContext.getSystemService(Context.STORAGE_SERVICE);
- StorageVolume[] volumes = storageManager.getVolumeList();
- if (volumes.length > 0) {
- massStorageSupported = volumes[0].allowMassStorage();
- }
+ final StorageManager storageManager = StorageManager.from(mContext);
+ final StorageVolume primary = storageManager.getPrimaryVolume();
+ massStorageSupported = primary != null && primary.allowMassStorage();
mUseUsbNotification = !massStorageSupported;
// make sure the ADB_ENABLED setting value matches the current state
@@ -425,6 +428,9 @@ public class UsbDeviceManager {
setEnabledFunctions(mDefaultFunctions, true);
updateAdbNotification();
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mAdbEnabled);
+ }
}
private void setEnabledFunctions(String functions, boolean makeDefault) {
@@ -532,7 +538,7 @@ public class UsbDeviceManager {
}
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void updateAudioSourceFunction() {
@@ -555,7 +561,7 @@ public class UsbDeviceManager {
Slog.e(TAG, "could not open audio source PCM file", e);
}
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
mAudioSourceEnabled = enabled;
}
}
@@ -601,6 +607,9 @@ public class UsbDeviceManager {
if (mCurrentAccessory != null) {
mSettingsManager.accessoryAttached(mCurrentAccessory);
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mAdbEnabled);
+ }
break;
}
}
@@ -802,10 +811,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/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 9b3459b..a8453d3 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -35,6 +35,7 @@ import android.hardware.usb.UsbManager;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Process;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.Xml;
@@ -546,7 +547,7 @@ class UsbSettingsManager {
}
// Send broadcast to running activity with registered intent
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
// Start activity with registered intent
resolveActivity(intent, matches, defaultPackage, device, null);
@@ -559,7 +560,7 @@ class UsbSettingsManager {
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
public void accessoryAttached(UsbAccessory accessory) {
@@ -586,7 +587,7 @@ class UsbSettingsManager {
Intent intent = new Intent(
UsbManager.ACTION_USB_ACCESSORY_DETACHED);
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index 13e8bc5..2445b98 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -4,16 +4,15 @@ package com.android.server.wm;
import android.graphics.Matrix;
import android.util.Slog;
+import android.view.Display;
import android.view.Surface;
import android.view.WindowManagerPolicy;
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 +47,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 +125,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);
}
}
@@ -133,11 +135,13 @@ public class AppWindowAnimator {
thumbnailTransformation.clear();
thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
- final boolean screenAnimation = mAnimator.mScreenRotationAnimation != null
- && mAnimator.mScreenRotationAnimation.isAnimating();
+
+ ScreenRotationAnimation screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ final boolean screenAnimation = screenRotationAnimation != null
+ && screenRotationAnimation.isAnimating();
if (screenAnimation) {
- thumbnailTransformation.postCompose(
- mAnimator.mScreenRotationAnimation.getEnterTransformation());
+ thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation());
}
// cache often used attributes locally
final float tmpFloats[] = mService.mTmpFloats;
@@ -168,7 +172,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;
@@ -233,10 +237,8 @@ public class AppWindowAnimator {
return false;
}
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("AppWindowToken", mAnimator.mPendingLayoutChanges);
- }
+ mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "AppWindowToken");
clearAnimation();
animating = false;
@@ -255,9 +257,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 +268,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..5b77b20 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;
@@ -43,14 +43,15 @@ public class BlackFrame {
int w = r-l;
int h = b-t;
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- surface = new WindowStateAnimator.SurfaceTrace(session, 0, "BlackSurface("
+ surface = new WindowStateAnimator.SurfaceTrace(session, "BlackSurface("
+ l + ", " + t + ")",
- -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
+ w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM | Surface.HIDDEN);
} else {
- surface = new Surface(session, 0, "BlackSurface",
- -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
+ surface = new Surface(session, "BlackSurface",
+ w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
surface.setAlpha(1);
+ surface.setLayerStack(layerStack);
surface.setLayer(layer);
surface.show();
if (WindowManagerService.SHOW_TRANSACTIONS ||
@@ -103,7 +104,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 +112,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..9bca834 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -39,24 +39,25 @@ 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,
+ mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
"DimAnimator",
- -1, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
} else {
- mDimSurface = new Surface(session, 0,
- "DimAnimator",
- -1, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ mDimSurface = new Surface(session, "DimAnimator",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
if (WindowManagerService.SHOW_TRANSACTIONS ||
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
" DIM " + mDimSurface + ": CREATE");
+ mDimSurface.setLayerStack(layerStack);
mDimSurface.setAlpha(0.0f);
+ mDimSurface.show();
} catch (Exception e) {
Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
}
@@ -211,5 +212,20 @@ class DimAnimator {
mDimHeight = dimHeight;
mDimTarget = dimTarget;
}
+
+ Parameters(Parameters o) {
+ mDimWinAnimator = o.mDimWinAnimator;
+ mDimWidth = o.mDimWidth;
+ mDimHeight = o.mDimHeight;
+ mDimTarget = o.mDimTarget;
+ }
+
+ public void printTo(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("mDimWinAnimator="); pw.print(mDimWinAnimator.mWin.mAttrs.getTitle());
+ pw.print(" "); pw.print(mDimWidth); pw.print(" x ");
+ pw.print(mDimHeight);
+ pw.print(" mDimTarget="); pw.println(mDimTarget);
+ }
}
-} \ No newline at end of file
+}
diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java
index 9fca418..ddbd70d 100644
--- a/services/java/com/android/server/wm/DimSurface.java
+++ b/services/java/com/android/server/wm/DimSurface.java
@@ -30,24 +30,25 @@ 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,
+ mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
"DimSurface",
- -1, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
} else {
- mDimSurface = new Surface(session, 0,
- "DimSurface",
- -1, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ mDimSurface = new Surface(session, "DimSurface",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
if (WindowManagerService.SHOW_TRANSACTIONS ||
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
" DIM " + mDimSurface + ": CREATE");
+ mDimSurface.setLayerStack(layerStack);
mDimSurface.setAlpha(0.0f);
+ mDimSurface.show();
} catch (Exception e) {
Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
}
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..3898ebc
--- /dev/null
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -0,0 +1,133 @@
+/*
+ * 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.os.RemoteCallbackList;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.IDisplayContentChangeListener;
+
+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();
+
+ // Specification for magnifying the display content.
+ MagnificationSpec mMagnificationSpec;
+
+ // Callback for observing content changes on a display.
+ RemoteCallbackList<IDisplayContentChangeListener> mDisplayContentChangeListeners;
+
+ // 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;
+
+ // Accessed directly by all users.
+ boolean layoutNeeded;
+ int pendingLayoutChanges;
+ final boolean isDefaultDisplay;
+
+ DisplayContent(Display display) {
+ mDisplay = display;
+ mDisplayId = display.getDisplayId();
+ display.getDisplayInfo(mDisplayInfo);
+ isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ WindowList getWindowList() {
+ return mWindows;
+ }
+
+ Display getDisplay() {
+ return mDisplay;
+ }
+
+ DisplayInfo getDisplayInfo() {
+ return mDisplayInfo;
+ }
+
+ public void updateDisplayInfo() {
+ mDisplay.getDisplayInfo(mDisplayInfo);
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
+ final String subPrefix = " " + prefix;
+ pw.print(subPrefix); 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.print(subPrefix); pw.print("layoutNeeded="); pw.print(layoutNeeded);
+ if (mMagnificationSpec != null) {
+ pw.print(" mMagnificationSpec="); pw.print(mMagnificationSpec);
+ }
+ pw.println();
+ }
+}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index b2cf3e0..545fce5 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -23,12 +23,14 @@ import com.android.server.wm.WindowManagerService.H;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.graphics.Point;
import android.graphics.Region;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.Display;
import android.view.DragEvent;
import android.view.InputChannel;
import android.view.Surface;
@@ -58,6 +60,7 @@ class DragState {
WindowState mTargetWindow;
ArrayList<WindowState> mNotifiedWindows;
boolean mDragInProgress;
+ Display mDisplay;
private final Region mTmpRegion = new Region();
@@ -84,7 +87,11 @@ class DragState {
mNotifiedWindows = null;
}
- void register() {
+ /**
+ * @param display The Display that the window being dragged is on.
+ */
+ void register(Display display) {
+ mDisplay = display;
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 +108,8 @@ class DragState {
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null);
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+ mDisplay.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.inputChannel = mServerChannel;
mDragWindowHandle.layer = getDragLayerLw();
@@ -125,8 +133,10 @@ class DragState {
// The drag window covers the entire display
mDragWindowHandle.frameLeft = 0;
mDragWindowHandle.frameTop = 0;
- mDragWindowHandle.frameRight = mService.mCurDisplayWidth;
- mDragWindowHandle.frameBottom = mService.mCurDisplayHeight;
+ Point p = new Point();
+ mDisplay.getRealSize(p);
+ mDragWindowHandle.frameRight = p.x;
+ mDragWindowHandle.frameBottom = p.y;
// Pause rotations before a drag.
if (WindowManagerService.DEBUG_ORIENTATION) {
@@ -179,9 +189,10 @@ class DragState {
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
- final int N = mService.mWindows.size();
+ final WindowList windows = mService.getWindowListLocked(mDisplay);
+ 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 +391,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 = mService.getWindowListLocked(mDisplay);
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..aa18ee4 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -19,11 +19,13 @@ package com.android.server.wm;
import com.android.server.input.InputManagerService;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.AllWindowsIterator;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
+import android.view.Display;
import android.view.InputChannel;
import android.view.KeyEvent;
import android.view.WindowManager;
@@ -128,7 +130,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 +141,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 +203,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,9 +227,11 @@ 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--) {
- final WindowState child = windows.get(i);
+ // Add all windows on the default display.
+ final AllWindowsIterator iterator = mService.new AllWindowsIterator(
+ WindowManagerService.REVERSE_ITERATOR);
+ while (iterator.hasNext()) {
+ final WindowState child = iterator.next();
final InputChannel inputChannel = child.mInputChannel;
final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
@@ -202,46 +246,28 @@ final class InputMonitor implements InputManagerService.Callbacks {
final boolean isVisible = child.isVisibleLw();
final boolean hasWallpaper = (child == mService.mWallpaperTarget)
&& (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
+ final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
// If there's a drag in progress and 'child' is a potential drop target,
// make sure it's been told about the drag
- if (inDrag && isVisible) {
+ if (inDrag && isVisible && onDefaultDisplay) {
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 && onDefaultDisplay) {
+ 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/MagnificationSpec.java b/services/java/com/android/server/wm/MagnificationSpec.java
new file mode 100644
index 0000000..31aae66
--- /dev/null
+++ b/services/java/com/android/server/wm/MagnificationSpec.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+public class MagnificationSpec {
+ public float mScale = 1.0f;
+ public float mOffsetX;
+ public float mOffsetY;
+
+ public void initialize(float scale, float offsetX, float offsetY) {
+ mScale = scale;
+ mOffsetX = offsetX;
+ mOffsetY = offsetY;
+ }
+
+ public boolean isNop() {
+ return mScale == 1.0f && mOffsetX == 0 && mOffsetY == 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("<scale:");
+ builder.append(mScale);
+ builder.append(",offsetX:");
+ builder.append(mOffsetX);
+ builder.append(",offsetY:");
+ builder.append(mOffsetY);
+ builder.append(">");
+ return builder.toString();
+ }
+}
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 938db9e..8d2e2e8 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,11 +42,13 @@ class ScreenRotationAnimation {
static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
final Context mContext;
+ final Display mDisplay;
Surface mSurface;
BlackFrame mCustomBlackFrame;
BlackFrame mExitingBlackFrame;
BlackFrame mEnteringBlackFrame;
int mWidth, mHeight;
+ int mExitAnimId, mEnterAnimId;
int mOriginalRotation;
int mOriginalWidth, mOriginalHeight;
@@ -185,9 +188,13 @@ class ScreenRotationAnimation {
pw.println();
}
- public ScreenRotationAnimation(Context context, SurfaceSession session,
- boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) {
+ public ScreenRotationAnimation(Context context, Display display, SurfaceSession session,
+ boolean inTransaction, int originalWidth, int originalHeight, int originalRotation,
+ int exitAnim, int enterAnim) {
mContext = context;
+ mDisplay = display;
+ mExitAnimId = exitAnim;
+ mEnterAnimId = enterAnim;
// Screenshot does NOT include rotation!
if (originalRotation == Surface.ROTATION_90
@@ -212,17 +219,20 @@ 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, "FreezeSurface",
+ 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, "FreezeSurface",
+ mWidth, mHeight,
+ PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
}
if (!mSurface.isValid()) {
// Screenshot failed, punt.
mSurface = null;
return;
}
+ mSurface.setLayerStack(mDisplay.getLayerStack());
mSurface.setLayer(FREEZE_LAYER + 1);
mSurface.setAlpha(0);
mSurface.show();
@@ -234,7 +244,7 @@ class ScreenRotationAnimation {
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
" FREEZE " + mSurface + ": CREATE");
- setRotation(originalRotation);
+ setRotationInTransaction(originalRotation);
} finally {
if (!inTransaction) {
Surface.closeTransaction();
@@ -254,7 +264,7 @@ class ScreenRotationAnimation {
return delta;
}
- void setSnapshotTransform(Matrix matrix, float alpha) {
+ private void setSnapshotTransformInTransaction(Matrix matrix, float alpha) {
if (mSurface != null) {
matrix.getValues(mTmpFloats);
mSurface.setPosition(mTmpFloats[Matrix.MTRANS_X],
@@ -297,7 +307,7 @@ class ScreenRotationAnimation {
}
// Must be called while in a transaction.
- private void setRotation(int rotation) {
+ private void setRotationInTransaction(int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
@@ -307,13 +317,13 @@ class ScreenRotationAnimation {
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
- setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
+ setSnapshotTransformInTransaction(mSnapshotInitialMatrix, 1.0f);
}
// Must be called while in a transaction.
- public boolean setRotation(int rotation, SurfaceSession session,
+ public boolean setRotationInTransaction(int rotation, SurfaceSession session,
long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
- setRotation(rotation);
+ setRotationInTransaction(rotation);
if (TWO_PHASE_ANIMATION) {
return startAnimation(session, maxAnimationDuration, animationScale,
finalWidth, finalHeight, false);
@@ -369,58 +379,68 @@ class ScreenRotationAnimation {
+ finalWidth + " finalHeight=" + finalHeight
+ " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight);
- switch (delta) {
- case Surface.ROTATION_0:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_0_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_0_enter);
- if (USE_CUSTOM_BLACK_FRAME) {
- mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_0_frame);
- }
- break;
- case Surface.ROTATION_90:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_plus_90_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_plus_90_enter);
- if (USE_CUSTOM_BLACK_FRAME) {
- mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_plus_90_frame);
- }
- break;
- case Surface.ROTATION_180:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_180_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_180_enter);
- mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_180_frame);
- break;
- case Surface.ROTATION_270:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_minus_90_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_minus_90_enter);
- if (USE_CUSTOM_BLACK_FRAME) {
- mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.screen_rotate_minus_90_frame);
- }
- break;
+ final boolean customAnim;
+ if (mExitAnimId != 0 && mEnterAnimId != 0) {
+ customAnim = true;
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, mExitAnimId);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, mEnterAnimId);
+ } else {
+ customAnim = false;
+ switch (delta) {
+ case Surface.ROTATION_0:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_0_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_0_enter);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_0_frame);
+ }
+ break;
+ case Surface.ROTATION_90:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_plus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_plus_90_enter);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_plus_90_frame);
+ }
+ break;
+ case Surface.ROTATION_180:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_180_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_180_enter);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_180_frame);
+ }
+ break;
+ case Surface.ROTATION_270:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_minus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_minus_90_enter);
+ if (USE_CUSTOM_BLACK_FRAME) {
+ mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_minus_90_frame);
+ }
+ break;
+ }
}
- // Compute partial steps between original and final sizes. These
- // are used for the dimensions of the exiting and entering elements,
- // so they are never stretched too significantly.
- final int halfWidth = (finalWidth + mOriginalWidth) / 2;
- final int halfHeight = (finalHeight + mOriginalHeight) / 2;
-
// Initialize the animations. This is a hack, redefining what "parent"
// means to allow supplying the last and next size. In this definition
// "%p" is the original (let's call it "previous") size, and "%" is the
// screen's current/new size.
if (TWO_PHASE_ANIMATION && firstStart) {
+ // Compute partial steps between original and final sizes. These
+ // are used for the dimensions of the exiting and entering elements,
+ // so they are never stretched too significantly.
+ final int halfWidth = (finalWidth + mOriginalWidth) / 2;
+ final int halfHeight = (finalHeight + mOriginalHeight) / 2;
+
if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
mStartEnterAnimation.initialize(finalWidth, finalHeight,
halfWidth, halfHeight);
@@ -472,6 +492,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 +511,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);
@@ -502,25 +524,25 @@ class ScreenRotationAnimation {
}
}
- if (mExitingBlackFrame == null) {
+ if (!customAnim && mExitingBlackFrame == null) {
if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
WindowManagerService.TAG,
">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
Surface.openTransaction();
-
- // Compute the transformation matrix that must be applied
- // the the black frame to make it stay in the initial position
- // before the new screen rotation. This is different than the
- // snapshot transformation because the snapshot is always based
- // of the native orientation of the screen, not the orientation
- // we were last in.
- createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
-
try {
+ // Compute the transformation matrix that must be applied
+ // the the black frame to make it stay in the initial position
+ // before the new screen rotation. This is different than the
+ // snapshot transformation because the snapshot is always based
+ // of the native orientation of the screen, not the orientation
+ // we were last in.
+ createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
+
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);
@@ -532,7 +554,7 @@ class ScreenRotationAnimation {
}
}
- if (false && mEnteringBlackFrame == null) {
+ if (customAnim && mEnteringBlackFrame == null) {
if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
WindowManagerService.TAG,
">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
@@ -542,7 +564,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 {
@@ -834,7 +857,7 @@ class ScreenRotationAnimation {
return more;
}
- void updateSurfaces() {
+ void updateSurfacesInTransaction() {
if (!mStarted) {
return;
}
@@ -874,7 +897,7 @@ class ScreenRotationAnimation {
}
}
- setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
+ setSnapshotTransformInTransaction(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
}
public boolean stepAnimationLocked(long now) {
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 61c0e9c..16beeab 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.getDisplay());
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mService.mDragState.mServerChannel)) {
@@ -389,6 +408,31 @@ 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);
+ }
+ }
+ }
+
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
+ synchronized(mService.mWindowMap) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.onRectangleOnScreenRequested(token, rectangle, immediate);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
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..90bbd08 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,13 +37,16 @@ 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, "StrictModeFlash",
+ 1, 1, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
} catch (Surface.OutOfResourcesException e) {
return;
}
+ mSurface.setLayerStack(display.getLayerStack());
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
mSurface.setPosition(0, 0);
+ mSurface.show();
mDrawNeeded = true;
}
diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java
index 5497eb4..ac152c9 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);
@@ -111,8 +113,9 @@ class Watermark {
mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
try {
- mSurface = new Surface(session, 0,
- "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+ mSurface = new Surface(session, "WatermarkSurface",
+ 1, 1, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ mSurface.setLayerStack(mDisplay.getLayerStack());
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100);
mSurface.setPosition(0, 0);
mSurface.show();
@@ -171,4 +174,4 @@ class Watermark {
}
}
}
-} \ No newline at end of file
+}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 62cf711..0a4e6d3 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -8,19 +8,23 @@ 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.util.SparseArray;
+import android.util.SparseIntArray;
+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,26 +36,20 @@ 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>();
-
boolean mAnimating;
- WindowState mWindowAnimationBackground;
- int mWindowAnimationBackgroundColor;
+
+ final Runnable mAnimationRunnable;
+
int mAdjResult;
- int mPendingLayoutChanges;
+ // Layout changes for individual Displays. Indexed by displayId.
+ SparseIntArray mPendingLayoutChanges = new SparseIntArray();
+ // TODO: Assign these from each iteration through DisplayContent. Only valid between loops.
/** Overall window dimensions */
int mDw, mDh;
@@ -65,105 +63,211 @@ public class WindowAnimator {
* is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
private int mAnimTransactionSequence;
- /** The one and only screen rotation if one is happening */
- ScreenRotationAnimation mScreenRotationAnimation = null;
-
// 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;
- DimAnimator.Parameters mDimParams = null;
+ SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators =
+ new SparseArray<WindowAnimator.DisplayContentsAnimator>();
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;
+ SparseIntArray mPendingLayoutChanges;
+ WindowState mWindowDetachedWallpaper;
+ }
+ /** Do not modify unless holding mService.mWindowMap or this and mAnimToLayout in that order */
+ final AnimatorToLayoutParams mAnimToLayout = new AnimatorToLayoutParams();
+
+ boolean mInitialized = false;
+
+ // forceHiding states.
+ static final int KEYGUARD_NOT_SHOWN = 0;
+ static final int KEYGUARD_ANIMATING_IN = 1;
+ static final int KEYGUARD_SHOWN = 2;
+ static final int KEYGUARD_ANIMATING_OUT = 3;
+ int mForceHiding = KEYGUARD_NOT_SHOWN;
+
+ private String forceHidingToString() {
+ switch (mForceHiding) {
+ case KEYGUARD_NOT_SHOWN: return "KEYGUARD_NOT_SHOWN";
+ case KEYGUARD_ANIMATING_IN: return "KEYGUARD_ANIMATING_IN";
+ case KEYGUARD_SHOWN: return "KEYGUARD_SHOWN";
+ case KEYGUARD_ANIMATING_OUT:return "KEYGUARD_ANIMATING_OUT";
+ default: return "KEYGUARD STATE UNKNOWN " + mForceHiding;
+ }
}
- 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;
+ 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 addDisplayLocked(final int displayId) {
+ DisplayContentsAnimator displayAnimator = getDisplayContentsAnimatorLocked(displayId);
+ displayAnimator.mWindowAnimationBackgroundSurface =
+ new DimSurface(mService.mFxSession, displayId);
+ displayAnimator.mDimAnimator = new DimAnimator(mService.mFxSession, displayId);
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mInitialized = true;
}
}
- 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;
+ void removeDisplayLocked(final int displayId) {
+ mDisplayContentsAnimators.delete(displayId);
+ }
+
+ /** Locked on mAnimToLayout */
+ void updateAnimToLayoutLocked() {
+ final AnimatorToLayoutParams animToLayout = mAnimToLayout;
+ synchronized (animToLayout) {
+ animToLayout.mBulkUpdateParams = mBulkUpdateParams;
+ animToLayout.mPendingLayoutChanges = mPendingLayoutChanges.clone();
+ animToLayout.mWindowDetachedWallpaper = mWindowDetachedWallpaper;
+
+ if (!animToLayout.mUpdateQueued) {
+ animToLayout.mUpdateQueued = true;
+ mService.mH.sendMessage(mService.mH.obtainMessage(UPDATE_ANIM_PARAMETERS));
+ }
}
+ }
- 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;
+ /** 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);
+ }
+
+ 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.
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
+
+ displayAnimator.mWinAnimators.clear();
+ final WinAnimatorList winAnimators = layoutToAnim.mWinAnimatorLists.get(displayId);
+ if (winAnimators != null) {
+ displayAnimator.mWinAnimators.addAll(winAnimators);
+ }
+
+ DimAnimator.Parameters dimParams = layoutToAnim.mDimParams.get(displayId);
+ if (dimParams == null) {
+ displayAnimator.mDimParams = null;
+ } else {
+ final WindowStateAnimator newWinAnimator = dimParams.mDimWinAnimator;
+
+ // Only set dim params on the highest dimmed layer.
+ final WindowStateAnimator existingDimWinAnimator =
+ displayAnimator.mDimParams == null ?
+ null : displayAnimator.mDimParams.mDimWinAnimator;
+ // Don't turn on for an unshown surface, or for any layer but the highest
+ // dimmed layer.
+ if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
+ || !existingDimWinAnimator.mSurfaceShown
+ || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
+ displayAnimator.mDimParams = new DimAnimator.Parameters(dimParams);
}
}
}
- if (mWindowAnimationBackgroundSurface == null) {
- mWindowAnimationBackgroundSurface = new DimSurface(mService.mFxSession);
+
+ 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);
}
- 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;
+ 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);
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
+ }
+ }
+ token.hidden = true;
+ }
+ }
+ }
+
+ private void updateAppWindowsLocked() {
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)) {
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("appToken " + appAnimator.mAppToken + " done",
- mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating " + appAnimator.mAppToken);
}
@@ -178,36 +282,26 @@ public class WindowAnimator {
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("exiting appToken " + appAnimator.mAppToken
- + " done", mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "exiting appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
}
}
-
- if (mScreenRotationAnimation != null && mScreenRotationAnimation.isAnimating()) {
- if (mScreenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
- mAnimating = true;
- } else {
- mBulkUpdateParams |= SET_UPDATE_ROTATION;
- mScreenRotationAnimation.kill();
- mScreenRotationAnimation = null;
- }
- }
}
- private void updateWindowsAndWallpaperLocked() {
+ private void updateWindowsLocked(final int displayId) {
++mAnimTransactionSequence;
+ final WinAnimatorList winAnimatorList =
+ getDisplayContentsAnimatorLocked(displayId).mWinAnimators;
ArrayList<WindowStateAnimator> unForceHiding = null;
boolean wallpaperInUnForceHiding = false;
+ mForceHiding = KEYGUARD_NOT_SHOWN;
- for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
- WindowState win = mService.mWindows.get(i);
- WindowStateAnimator winAnimator = win.mWinAnimator;
+ 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,56 +313,13 @@ 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;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
@@ -278,10 +329,11 @@ public class WindowAnimator {
WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
"Animation started that could impact force hide: " + win);
mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
mService.mFocusMayChange = true;
}
@@ -326,7 +378,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,12 +390,13 @@ 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;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
}
@@ -353,16 +406,16 @@ public class WindowAnimator {
if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
if (atoken == null || atoken.allDrawn) {
if (winAnimator.performShowLocked()) {
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ mPendingLayoutChanges.put(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
}
}
}
- 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,38 +441,134 @@ public class WindowAnimator {
}
}
+ private void updateWallpaperLocked(int displayId) {
+ final DisplayContentsAnimator displayAnimator =
+ getDisplayContentsAnimatorLocked(displayId);
+ final WinAnimatorList winAnimatorList = displayAnimator.mWinAnimators;
+ WindowStateAnimator windowAnimationBackground = null;
+ int windowAnimationBackgroundColor = 0;
+ WindowState detachedWallpaper = null;
+ final DimSurface windowAnimationBackgroundSurface =
+ displayAnimator.mWindowAnimationBackgroundSurface;
+
+ 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;
+ }
+ }
+ }
+
+ windowAnimationBackgroundSurface.show(mDw, mDh,
+ animLayer - WindowManagerService.LAYER_OFFSET_DIM,
+ windowAnimationBackgroundColor);
+ } else {
+ windowAnimationBackgroundSurface.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 "
+ wtoken + " numInteresting=" + wtoken.numInterestingWindows
+ " numDrawn=" + wtoken.numDrawnWindows);
// This will set mOrientationChangeComplete and cause a pass through layout.
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "testTokenMayBeDrawnLocked: freezingScreen");
} else {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("testTokenMayBeDrawnLocked",
- mPendingLayoutChanges);
- }
-
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "testTokenMayBeDrawnLocked");
+
// We can now show all of the drawn windows!
if (!mService.mOpeningApps.contains(wtoken)) {
- mAnimating |= wtoken.mAppAnimator.showAllWindowsLocked();
+ mAnimating |= appAnimator.showAllWindowsLocked();
}
}
}
@@ -427,64 +576,82 @@ public class WindowAnimator {
}
}
- private void performAnimationsLocked() {
- mForceHiding = KEYGUARD_NOT_SHOWN;
- mDetachedWallpaper = null;
- mWindowAnimationBackground = null;
- mWindowAnimationBackgroundColor = 0;
+ private void performAnimationsLocked(final int displayId) {
+ updateWindowsLocked(displayId);
+ updateWallpaperLocked(displayId);
+ }
- updateWindowsAndWallpaperLocked();
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- mPendingActions |= WALLPAPER_ACTION_PENDING;
+ // TODO(cmautner): Change the following comment when no longer locked on mWindowMap */
+ /** Locked on mService.mWindowMap and this. */
+ private void animateLocked() {
+ if (!mInitialized) {
+ return;
}
- testTokenMayBeDrawnLocked();
- }
-
- synchronized void animate() {
- mPendingLayoutChanges = 0;
+ mPendingLayoutChanges.clear();
mCurrentTime = SystemClock.uptimeMillis();
- mBulkUpdateParams = 0;
+ mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
boolean wasAnimating = mAnimating;
mAnimating = false;
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- // Update animations of all applications, including those
- // associated with exiting/removed apps
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, ">>> OPEN TRANSACTION animateLocked");
Surface.openTransaction();
-
try {
- updateWindowsAppsAndRotationAnimationsLocked();
- performAnimationsLocked();
- testWallpaperAndBackgroundLocked();
+ updateAppWindowsLocked();
+
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
+
+ final ScreenRotationAnimation screenRotationAnimation =
+ displayAnimator.mScreenRotationAnimation;
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+ if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
+ mAnimating = true;
+ } else {
+ mBulkUpdateParams |= SET_UPDATE_ROTATION;
+ screenRotationAnimation.kill();
+ displayAnimator.mScreenRotationAnimation = null;
+ }
+ }
- // THIRD LOOP: Update the surfaces of all windows.
+ // Update animations of all applications, including those
+ // associated with exiting/removed apps
+ performAnimationsLocked(displayId);
- if (mScreenRotationAnimation != null) {
- mScreenRotationAnimation.updateSurfaces();
+ final WinAnimatorList winAnimatorList = displayAnimator.mWinAnimators;
+ final int N = winAnimatorList.size();
+ for (int j = 0; j < N; j++) {
+ winAnimatorList.get(j).prepareSurfaceLocked(true);
+ }
}
- final int N = mWinAnimators.size();
- for (int i = 0; i < N; i++) {
- mWinAnimators.get(i).prepareSurfaceLocked(true);
- }
+ testTokenMayBeDrawnLocked();
- if (mDimParams != null) {
- mDimAnimator.updateParameters(mContext.getResources(), mDimParams, mCurrentTime);
- }
- if (mDimAnimator != null && mDimAnimator.mDimShown) {
- mAnimating |= mDimAnimator.updateSurface(isDimming(), mCurrentTime,
- !mService.okToDisplay());
- }
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
- if (mService.mBlackFrame != null) {
- if (mScreenRotationAnimation != null) {
- mService.mBlackFrame.setMatrix(
- mScreenRotationAnimation.getEnterTransformation().getMatrix());
- } else {
- mService.mBlackFrame.clearMatrix();
+ final ScreenRotationAnimation screenRotationAnimation =
+ displayAnimator.mScreenRotationAnimation;
+ if (screenRotationAnimation != null) {
+ screenRotationAnimation.updateSurfacesInTransaction();
+ }
+
+ final DimAnimator.Parameters dimParams = displayAnimator.mDimParams;
+ final DimAnimator dimAnimator = displayAnimator.mDimAnimator;
+ if (dimParams != null) {
+ dimAnimator.updateParameters(
+ mContext.getResources(), dimParams, mCurrentTime);
+ }
+ if (dimAnimator != null && dimAnimator.mDimShown) {
+ mAnimating |= dimAnimator.updateSurface(isDimmingLocked(displayId),
+ mCurrentTime, !mService.okToDisplay());
}
}
@@ -495,19 +662,33 @@ public class WindowAnimator {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
Surface.closeTransaction();
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, "<<< CLOSE TRANSACTION animateLocked");
}
- mService.bulkSetParameters(mBulkUpdateParams, mPendingLayoutChanges);
+ for (int i = mPendingLayoutChanges.size() - 1; i >= 0; i--) {
+ if ((mPendingLayoutChanges.valueAt(i)
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ mPendingActions |= WALLPAPER_ACTION_PENDING;
+ }
+ }
+
+ if (mBulkUpdateParams != 0 || mPendingLayoutChanges.size() > 0) {
+ updateAnimToLayoutLocked();
+ }
if (mAnimating) {
- mService.scheduleAnimationLocked();
+ synchronized (mService.mLayoutToAnim) {
+ mService.scheduleAnimationLocked();
+ }
} else if (wasAnimating) {
mService.requestTraversalLocked();
}
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges=" + Integer.toHexString(mPendingLayoutChanges));
+ + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+ + Integer.toHexString(mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY)));
}
}
@@ -524,72 +705,107 @@ 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 isDimmingLocked(int displayId) {
+ return getDisplayContentsAnimatorLocked(displayId).mDimParams != null;
}
- boolean isDimming() {
- return mDimParams != null;
+ boolean isDimmingLocked(final WindowStateAnimator winAnimator) {
+ DimAnimator.Parameters dimParams =
+ getDisplayContentsAnimatorLocked(winAnimator.mWin.getDisplayId()).mDimParams;
+ return dimParams != null && dimParams.mDimWinAnimator == winAnimator;
}
- boolean isDimming(final WindowStateAnimator winAnimator) {
- return mDimParams != null && mDimParams.mDimWinAnimator == winAnimator;
- }
-
- public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
if (dumpAll) {
if (mWindowDetachedWallpaper != null) {
pw.print(prefix); pw.print("mWindowDetachedWallpaper=");
- pw.println(mWindowDetachedWallpaper);
+ pw.println(mWindowDetachedWallpaper);
}
pw.print(prefix); pw.print("mAnimTransactionSequence=");
- pw.println(mAnimTransactionSequence);
- if (mWindowAnimationBackgroundSurface != null) {
- pw.print(prefix); pw.print("mWindowAnimationBackgroundSurface:");
- mWindowAnimationBackgroundSurface.printTo(prefix + " ", pw);
+ pw.print(mAnimTransactionSequence);
+ pw.println(" mForceHiding=" + forceHidingToString());
+ for (int i = 0; i < mDisplayContentsAnimators.size(); i++) {
+ pw.print(prefix); pw.print("DisplayContentsAnimator #");
+ pw.println(mDisplayContentsAnimators.keyAt(i));
+ DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
+ final String subPrefix = " " + prefix;
+ final String subSubPrefix = " " + subPrefix;
+ if (displayAnimator.mWindowAnimationBackgroundSurface != null) {
+ pw.println(subPrefix + "mWindowAnimationBackgroundSurface:");
+ displayAnimator.mWindowAnimationBackgroundSurface.printTo(subSubPrefix, pw);
+ }
+ if (displayAnimator.mDimAnimator != null) {
+ pw.println(subPrefix + "mDimAnimator:");
+ displayAnimator.mDimAnimator.printTo(subSubPrefix, pw);
+ } else {
+ pw.println(subPrefix + "no DimAnimator ");
+ }
+ if (displayAnimator.mDimParams != null) {
+ pw.println(subPrefix + "mDimParams:");
+ displayAnimator.mDimParams.printTo(subSubPrefix, pw);
+ } else {
+ pw.println(subPrefix + "no DimParams ");
+ }
+ if (displayAnimator.mScreenRotationAnimation != null) {
+ pw.println(subPrefix + "mScreenRotationAnimation:");
+ displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
+ } else {
+ pw.print(subPrefix + "no ScreenRotationAnimation ");
+ }
}
- if (mDimAnimator != null) {
- pw.print(prefix); pw.print("mDimAnimator:");
- mDimAnimator.printTo(prefix + " ", pw);
- } else {
- pw.print(prefix); pw.print("no DimAnimator ");
+ pw.println();
+ }
+ }
+
+ void clearPendingActions() {
+ synchronized (this) {
+ mPendingActions = 0;
+ }
+ }
+
+ void setPendingLayoutChanges(final int displayId, final int changes) {
+ mPendingLayoutChanges.put(displayId, mPendingLayoutChanges.get(displayId) | changes);
+ }
+
+ void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String s) {
+ // Used to track which displays layout changes have been done.
+ SparseIntArray displays = new SparseIntArray();
+ for (int i = appAnimator.mAllAppWinAnimators.size() - 1; i >= 0; i--) {
+ WindowStateAnimator winAnimator = appAnimator.mAllAppWinAnimators.get(i);
+ final int displayId = winAnimator.mWin.mDisplayContent.getDisplayId();
+ if (displays.indexOfKey(displayId) < 0) {
+ setPendingLayoutChanges(displayId, changes);
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ mService.debugLayoutRepeats(s, mPendingLayoutChanges.get(displayId));
+ }
+ // Keep from processing this display again.
+ displays.put(displayId, changes);
}
}
}
- static class SetAnimationParams {
- final WindowStateAnimator mWinAnimator;
- final Animation mAnimation;
- final int mAnimDw;
- final int mAnimDh;
- public SetAnimationParams(final WindowStateAnimator winAnimator,
- final Animation animation, final int animDw, final int animDh) {
- mWinAnimator = winAnimator;
- mAnimation = animation;
- mAnimDw = animDw;
- mAnimDh = animDh;
+ private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
+ DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
+ if (displayAnimator == null) {
+ displayAnimator = new DisplayContentsAnimator();
+ mDisplayContentsAnimators.put(displayId, displayAnimator);
}
+ return displayAnimator;
+ }
+
+ void setScreenRotationAnimationLocked(int displayId, ScreenRotationAnimation animation) {
+ getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation = animation;
+ }
+
+ ScreenRotationAnimation getScreenRotationAnimationLocked(int displayId) {
+ return getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation;
}
- synchronized void clearPendingActions() {
- mPendingActions = 0;
+ private static class DisplayContentsAnimator {
+ WinAnimatorList mWinAnimators = new WinAnimatorList();
+ DimAnimator mDimAnimator = null;
+ DimAnimator.Parameters mDimParams = null;
+ DimSurface mWindowAnimationBackgroundSurface = null;
+ ScreenRotationAnimation mScreenRotationAnimation = null;
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 7011343..55a7c46 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,12 @@ 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.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 +56,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,42 +71,46 @@ 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;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
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.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.IDisplayContentChangeListener;
+import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindow;
@@ -119,8 +125,10 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowInfo;
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 +139,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,10 +160,12 @@ 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
- implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
+ implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
+ DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
@@ -267,58 +278,30 @@ 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);
+ 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 +366,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.
*/
@@ -451,10 +429,9 @@ public class WindowManagerService extends IWindowManager.Stub
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
- BlackFrame mBlackFrame;
-
final float[] mTmpFloats = new float[9];
+ boolean mDisplayReady;
boolean mSafeMode;
boolean mDisplayEnabled = false;
boolean mSystemBooted = false;
@@ -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,13 +452,13 @@ 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;
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
boolean mWaitingForConfig = false;
boolean mWindowsFreezingScreen = false;
+ boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -512,9 +468,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 +485,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,8 +502,6 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
- Display mDisplay;
-
boolean mIsTouchDevice;
final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -583,10 +538,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 +566,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 +583,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 +598,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 atoken = appAnimator.mAppToken;
+ mWinAnimators = new ArrayList<WindowStateAnimator>();
+ final int N = atoken.allAppWindows.size();
+ for (int i = 0; i < N; i++) {
+ mWinAnimators.add(atoken.allAppWindows.get(i).mWinAnimator);
+ }
+ }
+ }
+
+ static class LayoutToAnimatorParams {
+ boolean mParamsModified;
+
+ static final long WALLPAPER_TOKENS_CHANGED = 1 << 0;
+ long mChanges;
+
+ boolean mAnimationScheduled;
+ SparseArray<WinAnimatorList> mWinAnimatorLists = new SparseArray<WinAnimatorList>();
+ WindowState mWallpaperTarget;
+ WindowState mLowerWallpaperTarget;
+ WindowState mUpperWallpaperTarget;
+ SparseArray<DimAnimator.Parameters> mDimParams = new SparseArray<DimAnimator.Parameters>();
+ 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 +645,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 {
@@ -748,6 +716,9 @@ public class WindowManagerService extends IWindowManager.Stub
*/
boolean mInTouchMode = true;
+ // Temp regions for intermediary calculations.
+ private final Region mTempRegion = new Region();
+
private ViewServer mViewServer;
private ArrayList<WindowChangeListener> mWindowChangeListeners =
new ArrayList<WindowChangeListener>();
@@ -768,111 +739,38 @@ public class WindowManagerService extends IWindowManager.Stub
// For example, when this flag is true, there will be no wallpaper service.
final boolean mOnlyCore;
- public static WindowManagerService main(Context context,
- PowerManagerService pm, boolean haveInputMethods, boolean allowBootMsgs,
- boolean onlyCore) {
- WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs, onlyCore);
- thr.start();
-
- synchronized (thr) {
- while (thr.mService == null) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
+ public static WindowManagerService main(final Context context,
+ final PowerManagerService pm, final DisplayManagerService dm,
+ final Handler uiHandler, final Handler wmHandler,
+ final boolean haveInputMethods, final boolean showBootMsgs,
+ final boolean onlyCore) {
+ final WindowManagerService[] holder = new WindowManagerService[1];
+ wmHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ holder[0] = new WindowManagerService(context, pm, dm,
+ uiHandler, haveInputMethods, showBootMsgs, onlyCore);
}
- return thr.mService;
- }
+ }, 0);
+ return holder[0];
}
- static class WMThread extends Thread {
- WindowManagerService mService;
-
- private final Context mContext;
- private final PowerManagerService mPM;
- private final boolean mHaveInputMethods;
- private final boolean mAllowBootMessages;
- private final boolean mOnlyCore;
+ private void initPolicy(Handler uiHandler) {
+ uiHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
- public WMThread(Context context, PowerManagerService pm,
- boolean haveInputMethods, boolean allowBootMsgs, boolean onlyCore) {
- super("WindowManager");
- mContext = context;
- mPM = pm;
- mHaveInputMethods = haveInputMethods;
- mAllowBootMessages = allowBootMsgs;
- mOnlyCore = onlyCore;
- }
-
- @Override
- public void run() {
- Looper.prepare();
- //Looper.myLooper().setMessageLogging(new LogPrinter(
- // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
- WindowManagerService s = new WindowManagerService(mContext, mPM,
- mHaveInputMethods, mAllowBootMessages, mOnlyCore);
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_DISPLAY);
- android.os.Process.setCanSelfBackground(false);
-
- synchronized (this) {
- mService = s;
- notifyAll();
- }
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper");
- }
-
- Looper.loop();
- }
- }
-
- static class PolicyThread extends Thread {
- 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) {
- 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);
-
- synchronized (this) {
- mRunning = true;
- notifyAll();
- }
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode for PolicyThread's Looper");
+ mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
+ mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
}
-
- Looper.loop();
- }
+ }, 0);
}
private WindowManagerService(Context context, PowerManagerService pm,
+ DisplayManagerService displayManager, Handler uiHandler,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -880,7 +778,17 @@ 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;
+ mHeadless = displayManager.isHeadless();
+
+ mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager.registerDisplayListener(this, null);
+ Display[] displays = mDisplayManager.getDisplays();
+ for (Display display : displays) {
+ createDisplayContentLocked(display);
+ }
+
+ mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
mPowerManager = pm;
mPowerManager.setPolicy(mPolicy);
@@ -897,42 +805,37 @@ 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);
-
- PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
- thr.start();
+ mFxSession = new SurfaceSession();
+ mAnimator = new WindowAnimator(this);
- synchronized (thr) {
- while (!thr.mRunning) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
- }
- }
+ initPolicy(uiHandler);
mInputManager.start();
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
- mFxSession = new SurfaceSession();
Surface.openTransaction();
- createWatermark();
- Surface.closeTransaction();
+ try {
+ createWatermarkInTransaction();
+ } finally {
+ Surface.closeTransaction();
+ }
}
public InputManagerService getInputManagerService() {
@@ -955,34 +858,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;
}
@@ -990,49 +890,76 @@ public class WindowManagerService extends IWindowManager.Stub
return -1;
}
+ /**
+ * Return the list of Windows from the passed token on the given Display.
+ * @param token The token with all the windows.
+ * @param displayContent The display we are interested in.
+ * @return List of windows from token that are on displayContent.
+ */
+ WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) {
+ final WindowList windowList = new WindowList();
+ final int count = token.windows.size();
+ for (int i = 0; i < count; i++) {
+ final WindowState win = token.windows.get(i);
+ if (win.mDisplayContent == displayContent) {
+ windowList.add(win);
+ }
+ }
+ return windowList;
+ }
+
private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
- final ArrayList<WindowState> localmWindows = mWindows;
+ final DisplayContent displayContent = win.mDisplayContent;
- final int N = localmWindows.size();
+ final WindowList windows = win.getWindowList();
+ final int N = windows.size();
final WindowState attached = win.mAttachedWindow;
int i;
+ WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
if (attached == null) {
- int tokenWindowsPos = token.windows.size();
+ int tokenWindowsPos = 0;
+ int windowListPos = tokenWindowList.size();
if (token.appWindowToken != null) {
- int index = tokenWindowsPos-1;
+ int index = windowListPos - 1;
if (index >= 0) {
// If this application has existing windows, we
// simply place the new window on top of them... but
// keep the starting window on top.
if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
// Base windows go behind everything else.
- placeWindowBefore(token.windows.get(0), win);
- tokenWindowsPos = 0;
+ WindowState lowestWindow = tokenWindowList.get(0);
+ placeWindowBefore(lowestWindow, win);
+ tokenWindowsPos = token.windows.indexOf(lowestWindow);
} else {
AppWindowToken atoken = win.mAppToken;
- if (atoken != null &&
- token.windows.get(index) == atoken.startingWindow) {
- placeWindowBefore(token.windows.get(index), win);
- tokenWindowsPos--;
+ WindowState lastWindow = tokenWindowList.get(index);
+ if (atoken != null && lastWindow == atoken.startingWindow) {
+ placeWindowBefore(lastWindow, win);
+ tokenWindowsPos = token.windows.indexOf(lastWindow);
} else {
- int newIdx = findIdxBasedOnAppTokens(win);
- if(newIdx != -1) {
- //there is a window above this one associated with the same
- //apptoken note that the window could be a floating window
- //that was created later or a window at the top of the list of
- //windows associated with this token.
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
- Slog.v(TAG, "Adding window " + win + " at "
- + (newIdx+1) + " of " + N);
- }
- localmWindows.add(newIdx+1, win);
- mWindowsChanged = true;
+ int newIdx = findIdxBasedOnAppTokens(win);
+ //there is a window above this one associated with the same
+ //apptoken note that the window could be a floating window
+ //that was created later or a window at the top of the list of
+ //windows associated with this token.
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "Adding window " + win + " at "
+ + (newIdx + 1) + " of " + N);
+ }
+ windows.add(newIdx + 1, win);
+ if (newIdx < 0) {
+ // No window from token found on win's display.
+ tokenWindowsPos = 0;
+ } else {
+ tokenWindowsPos = token.windows.indexOf(windows.get(newIdx)) + 1;
}
+ mWindowsChanged = true;
}
}
} else {
+ // No windows from this token on this display
if (localLOGV) Slog.v(
TAG, "Figuring out where to add app window "
+ client.asBinder() + " (token=" + token + ")");
@@ -1048,10 +975,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
// We haven't reached the token yet; if this token
- // is not going to the bottom and has windows, we can
+ // is not going to the bottom and has windows on this display, we can
// use it as an anchor for when we do reach the token.
- if (!t.sendingToBottom && t.windows.size() > 0) {
- pos = t.windows.get(0);
+ tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent);
+ if (!t.sendingToBottom && tokenWindowList.size() > 0) {
+ pos = tokenWindowList.get(0);
}
}
// We now know the index into the apps. If we found
@@ -1061,9 +989,11 @@ public class WindowManagerService extends IWindowManager.Stub
// Move behind any windows attached to this one.
WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
- final int NC = atoken.windows.size();
+ tokenWindowList =
+ getTokenWindowsOnDisplay(atoken, win.mDisplayContent);
+ final int NC = tokenWindowList.size();
if (NC > 0) {
- WindowState bottom = atoken.windows.get(0);
+ WindowState bottom = tokenWindowList.get(0);
if (bottom.mSubLayer < 0) {
pos = bottom;
}
@@ -1072,12 +1002,13 @@ public class WindowManagerService extends IWindowManager.Stub
placeWindowBefore(pos, win);
} else {
// Continue looking down until we find the first
- // token that has windows.
+ // token that has windows on this display.
while (i >= 0) {
AppWindowToken t = mAnimatingAppTokens.get(i);
- final int NW = t.windows.size();
+ tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent);
+ final int NW = tokenWindowList.size();
if (NW > 0) {
- pos = t.windows.get(NW-1);
+ pos = tokenWindowList.get(NW-1);
break;
}
i--;
@@ -1100,7 +1031,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 +1040,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,18 +1049,18 @@ 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) {
- i++;
+ if (windows.get(i).mBaseLayer <= myLayer) {
break;
}
}
- if (i < 0) i = 0;
+ i++;
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);
@@ -1138,12 +1069,12 @@ public class WindowManagerService extends IWindowManager.Stub
} else {
// Figure out this window's ordering relative to the window
// it is attached to.
- final int NA = token.windows.size();
+ final int NA = tokenWindowList.size();
final int sublayer = win.mSubLayer;
int largestSublayer = Integer.MIN_VALUE;
WindowState windowWithLargestSublayer = null;
for (i=0; i<NA; i++) {
- WindowState w = token.windows.get(i);
+ WindowState w = tokenWindowList.get(i);
final int wSublayer = w.mSubLayer;
if (wSublayer >= largestSublayer) {
largestSublayer = wSublayer;
@@ -1157,8 +1088,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 {
@@ -1193,6 +1123,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.mAppToken != null && addToToken) {
win.mAppToken.allAppWindows.add(win);
}
+
+ if (windows.size() == 1) {
+ mDisplayManagerService.setDisplayHasContent(win.getDisplayId(), true);
+ }
}
/** TODO(cmautner): Is this the same as {@link WindowState#canReceiveKeys()} */
@@ -1226,13 +1160,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 = getDefaultWindowListLocked();
+ 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 +1184,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 +1204,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 +1219,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 +1277,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 +1290,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 +1304,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.
+ getDefaultWindowListLocked().add(pos, win);
mWindowsChanged = true;
moveInputMethodDialogsLocked(pos+1);
return;
@@ -1418,22 +1343,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 +1371,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 = getDefaultWindowListLocked();
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 +1401,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 +1420,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "Final window list:");
- logWindowList(" ");
+ logWindowList(windows, " ");
}
return;
}
@@ -1502,7 +1430,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 +1442,9 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ WindowList windows = getDefaultWindowListLocked();
+
int imPos = findDesiredInputMethodWindowIndexLocked(true);
if (imPos >= 0) {
// In this case, the input method windows are to be placed
@@ -1521,9 +1452,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 +1470,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 +1478,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 +1492,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 +1521,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 +1562,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 = getDefaultDisplayContentLocked().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 = getDefaultWindowListLocked();
+ int N = windows.size();
WindowState w = null;
WindowState foundW = null;
int foundI = 0;
@@ -1647,7 +1580,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 +1589,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 +1615,7 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
break;
- } else if (w == mAnimator.mWindowDetachedWallpaper) {
+ } else if (w == mWindowDetachedWallpaper) {
windowDetachedI = i;
}
}
@@ -1741,7 +1674,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 +1693,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 +1758,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 +1784,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) {
@@ -1876,7 +1809,7 @@ public class WindowManagerService extends IWindowManager.Stub
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContentLocked().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -1901,18 +1834,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 +1858,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 +1973,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 +2009,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 +2039,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) {
@@ -2131,7 +2052,7 @@ public class WindowManagerService extends IWindowManager.Stub
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContentLocked().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -2146,12 +2067,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 +2082,13 @@ public class WindowManagerService extends IWindowManager.Stub
long origId;
synchronized(mWindowMap) {
- if (mDisplay == null) {
+ if (!mDisplayReady) {
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 +2096,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 +2113,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 +2138,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 = getDisplayContentLocked(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();
@@ -2319,13 +2241,17 @@ public class WindowManagerService extends IWindowManager.Stub
win.mWinAnimator.mEnterAnimationPending = true;
- mPolicy.getContentInsetHintLw(attrs, outContentInsets);
+ if (displayContent.isDefaultDisplay) {
+ mPolicy.getContentInsetHintLw(attrs, outContentInsets);
+ } else {
+ outContentInsets.setEmpty();
+ }
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 +2269,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 +2283,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;
}
@@ -2423,13 +2349,14 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.mWinAnimator.applyAnimationLocked(transit, false)) {
win.mExiting = true;
}
+ scheduleNotifyWindowTranstionIfNeededLocked(win, transit);
}
if (win.mExiting || win.mWinAnimator.isAnimating()) {
// The exit animation is running... wait for it!
//Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
@@ -2485,7 +2412,12 @@ 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);
+ if (windows.isEmpty()) {
+ mDisplayManagerService.setDisplayHasContent(win.getDisplayId(), false);
+ }
mPendingRemove.remove(win);
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
@@ -2543,14 +2475,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (!mInLayout) {
- assignLayersLocked();
- mLayoutNeeded = true;
+ assignLayersLocked(windows);
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
}
-
+
mInputMonitor.updateInputWindowsLw(true /*force*/);
}
@@ -2610,7 +2542,7 @@ public class WindowManagerService extends IWindowManager.Stub
w.mGivenVisibleInsets.scale(w.mGlobalScale);
w.mGivenTouchableRegion.scale(w.mGlobalScale);
}
- mLayoutNeeded = true;
+ w.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -2683,6 +2615,80 @@ 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;
+ window.mDisplayContent.layoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
+ synchronized (mWindowMap) {
+ WindowState window = mWindowMap.get(token);
+ if (window != null) {
+ scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(window, rectangle,
+ immediate);
+ }
+ }
+ }
+
+ private void scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(WindowState window,
+ Rect rectangle, boolean immediate) {
+ DisplayContent displayContent = window.mDisplayContent;
+ if (displayContent.mDisplayContentChangeListeners != null
+ && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) {
+ mH.obtainMessage(H.NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, displayContent.getDisplayId(),
+ immediate? 1 : 0, new Rect(rectangle)).sendToTarget();
+ }
+ }
+
+ private void handleNotifyRectangleOnScreenRequested(int displayId, Rect rectangle,
+ boolean immediate) {
+ RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent == null) {
+ return;
+ }
+ callbacks = displayContent.mDisplayContentChangeListeners;
+ if (callbacks == null) {
+ return;
+ }
+ }
+ final int callbackCount = callbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < callbackCount; i++) {
+ try {
+ callbacks.getBroadcastItem(i).onRectangleOnScreenRequested(displayId,
+ rectangle, immediate);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ callbacks.finishBroadcast();
+ }
+ }
+
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
@@ -2729,7 +2735,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;
@@ -2772,9 +2778,10 @@ public class WindowManagerService extends IWindowManager.Stub
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;
- boolean focusMayChange = win.mViewVisibility != viewVisibility
+ final boolean isDefaultDisplay = win.isDefaultDisplay();
+ boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
- || (!win.mRelayoutCalled);
+ || (!win.mRelayoutCalled));
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -2848,7 +2855,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);
@@ -2856,7 +2863,7 @@ public class WindowManagerService extends IWindowManager.Stub
return 0;
}
if (toBeDisplayed) {
- focusMayChange = true;
+ focusMayChange = isDefaultDisplay;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& mInputMethodWindow == null) {
@@ -2893,7 +2900,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (win.isWinVisibleLw() &&
winAnimator.applyAnimationLocked(transit, false)) {
- focusMayChange = true;
+ focusMayChange = isDefaultDisplay;
win.mExiting = true;
} else if (win.mWinAnimator.isAnimating()) {
// Currently in a hide animation... turn this into
@@ -2911,6 +2918,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
winAnimator.destroySurfaceLocked();
}
+ scheduleNotifyWindowTranstionIfNeededLocked(win, transit);
}
}
@@ -2947,15 +2955,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- mLayoutNeeded = true;
- win.mGivenInsetsPending = (flags&WindowManagerImpl.RELAYOUT_INSETS_PENDING) != 0;
+ win.mDisplayContent.layoutNeeded = true;
+ 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 = getDefaultDisplayInfoLocked();
+ updateWallpaperOffsetLocked(win,
+ displayInfo.appWidth, displayInfo.appHeight, false);
}
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -2989,10 +2999,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) {
@@ -3035,20 +3045,118 @@ public class WindowManagerService extends IWindowManager.Stub
if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
adjustWallpaperWindowsLocked();
}
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
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;
}
}
+ @Override
+ public WindowInfo getWindowInfo(IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "getWindowInfo()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+ }
+ synchronized (mWindowMap) {
+ WindowState window = mWindowMap.get(token);
+ if (window != null) {
+ return getWindowInfoForWindowStateLocked(window);
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void getVisibleWindowsForDisplay(int displayId, List<WindowInfo> outInfos) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "getWindowInfos()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+ }
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent == null) {
+ return;
+ }
+ WindowList windows = displayContent.getWindowList();
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState window = windows.get(i);
+ if (window.isVisibleLw() ||
+ window.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
+ WindowInfo info = getWindowInfoForWindowStateLocked(window);
+ outInfos.add(info);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void magnifyDisplay(int displayId, float scale, float offsetX, float offsetY) {
+ if (!checkCallingPermission(
+ android.Manifest.permission.MAGNIFY_DISPLAY, "magnifyDisplay()")) {
+ throw new SecurityException("Requires MAGNIFY_DISPLAY permission");
+ }
+ synchronized (mWindowMap) {
+ MagnificationSpec spec = getDisplayMagnificationSpecLocked(displayId);
+ if (spec != null) {
+ final boolean scaleChanged = spec.mScale != scale;
+ final boolean offsetChanged = spec.mOffsetX != offsetX || spec.mOffsetY != offsetY;
+ if (!scaleChanged && !offsetChanged) {
+ return;
+ }
+ spec.initialize(scale, offsetX, offsetY);
+ // If the offset has changed we need to re-add the input windows
+ // since the offsets have to be propagated to the input system.
+ if (offsetChanged) {
+ // TODO(multidisplay): Input only occurs on the default display.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mInputMonitor.updateInputWindowsLw(true);
+ }
+ }
+ scheduleAnimationLocked();
+ }
+ }
+ }
+
+ MagnificationSpec getDisplayMagnificationSpecLocked(int displayId) {
+ DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent != null) {
+ if (displayContent.mMagnificationSpec == null) {
+ displayContent.mMagnificationSpec = new MagnificationSpec();
+ }
+ return displayContent.mMagnificationSpec;
+ }
+ return null;
+ }
+
+ private WindowInfo getWindowInfoForWindowStateLocked(WindowState window) {
+ WindowInfo info = WindowInfo.obtain();
+ info.token = window.mToken.token;
+ info.frame.set(window.mFrame);
+ info.type = window.mAttrs.type;
+ info.displayId = window.getDisplayId();
+ info.compatibilityScale = window.mGlobalScale;
+ info.visible = window.isVisibleLw()
+ || info.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND;
+ info.layer = window.mLayer;
+ window.getTouchableRegion(mTempRegion);
+ mTempRegion.getBounds(info.touchableRegion);
+ return info;
+ }
+
private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
+ (lp != null ? lp.packageName : null)
@@ -3128,12 +3236,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 +3277,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 = getDefaultDisplayInfoLocked();
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 +3302,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 +3318,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,66 +3325,106 @@ 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 = getDefaultDisplayInfoLocked();
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;
}
- private boolean applyAnimationLocked(AppWindowToken wtoken,
+ private boolean applyAnimationLocked(AppWindowToken atoken,
WindowManager.LayoutParams lp, int transit, boolean enter) {
// Only apply an animation if the display isn't frozen. If it is
// frozen, there is no reason to animate and it can cause strange
@@ -3289,27 +3437,29 @@ public class WindowManagerService extends IWindowManager.Stub
a = loadAnimation(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation: wtoken=" + wtoken
+ "applyAnimation: atoken=" + atoken
+ " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
- + " transit=" + transit + " Callers " + Debug.getCallers(3));
+ + " transit=" + transit + " isEntrance=" + enter
+ + " Callers " + Debug.getCallers(3));
} else if (mNextAppTransitionType == ActivityOptions.ANIM_SCALE_UP) {
a = createScaleUpAnimationLocked(transit, enter);
initialized = true;
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation: wtoken=" + wtoken
+ "applyAnimation: atoken=" + atoken
+ " 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);
+ + " transit=" + transit + " isEntrance=" + enter
+ + " Callers " + Debug.getCallers(3));
+ } 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";
- Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
+ String animName = scaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
+ Slog.v(TAG, "applyAnimation: atoken=" + atoken
+ " anim=" + a + " nextAppTransition=" + animName
- + " transit=" + transit + " Callers " + Debug.getCallers(3));
+ + " transit=" + transit + " isEntrance=" + enter
+ + " Callers " + Debug.getCallers(3));
}
} else {
int animAttr = 0;
@@ -3367,10 +3517,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
a = animAttr != 0 ? loadAnimation(lp, animAttr) : null;
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation: wtoken=" + wtoken
+ "applyAnimation: atoken=" + atoken
+ " anim=" + a
+ " animAttr=0x" + Integer.toHexString(animAttr)
- + " transit=" + transit + " Callers " + Debug.getCallers(3));
+ + " transit=" + transit + " isEntrance=" + enter
+ + " Callers " + Debug.getCallers(3));
}
if (a != null) {
if (DEBUG_ANIM) {
@@ -3379,15 +3530,15 @@ public class WindowManagerService extends IWindowManager.Stub
e = new RuntimeException();
e.fillInStackTrace();
}
- Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
+ Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e);
}
- wtoken.mAppAnimator.setAnimation(a, initialized);
+ atoken.mAppAnimator.setAnimation(a, initialized);
}
} else {
- wtoken.mAppAnimator.clearAnimation();
+ atoken.mAppAnimator.clearAnimation();
}
- return wtoken.mAppAnimator.animation != null;
+ return atoken.mAppAnimator.animation != null;
}
// -------------------------------------------------------------
@@ -3398,14 +3549,14 @@ public class WindowManagerService extends IWindowManager.Stub
int v = tokens.size()-1;
int m = mAppTokens.size()-1;
while (v >= 0 && m >= 0) {
- AppWindowToken wtoken = mAppTokens.get(m);
- if (wtoken.removed) {
+ AppWindowToken atoken = mAppTokens.get(m);
+ if (atoken.removed) {
m--;
continue;
}
- if (tokens.get(v) != wtoken.token) {
+ if (tokens.get(v) != atoken.token) {
Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
- + " @ " + v + ", internal is " + wtoken.token + " @ " + m);
+ + " @ " + v + ", internal is " + atoken.token + " @ " + m);
}
v--;
m--;
@@ -3415,9 +3566,9 @@ public class WindowManagerService extends IWindowManager.Stub
v--;
}
while (m >= 0) {
- AppWindowToken wtoken = mAppTokens.get(m);
- if (!wtoken.removed) {
- Slog.w(TAG, "Invalid internal token: " + wtoken.token + " @ " + m);
+ AppWindowToken atoken = mAppTokens.get(m);
+ if (!atoken.removed) {
+ Slog.w(TAG, "Invalid internal atoken: " + atoken.token + " @ " + m);
}
m--;
}
@@ -3440,7 +3591,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, msg);
return false;
}
-
+
boolean okToDisplay() {
return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOnFully();
}
@@ -3470,10 +3621,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,13 +3652,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (win.isVisibleNow()) {
- win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false);
+ win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
+ false);
+ scheduleNotifyWindowTranstionIfNeededLocked(win,
+ WindowManagerPolicy.TRANSIT_EXIT);
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
if (changed) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
false /*updateInputWindows*/);
@@ -3515,6 +3671,7 @@ public class WindowManagerService extends IWindowManager.Stub
mExitingTokens.add(wtoken);
} else if (wtoken.windowType == TYPE_WALLPAPER) {
mWallpaperTokens.remove(wtoken);
+ updateLayoutToAnimWallpaperTokens();
}
}
@@ -3530,18 +3687,18 @@ public class WindowManagerService extends IWindowManager.Stub
* Find the location to insert a new AppWindowToken into the window-ordered app token list.
* Note that mAppTokens.size() == mAnimatingAppTokens.size() + 1.
* @param addPos The location the token was inserted into in mAppTokens.
- * @param wtoken The token to insert.
+ * @param atoken The token to insert.
*/
- private void addAppTokenToAnimating(final int addPos, final AppWindowToken wtoken) {
+ private void addAppTokenToAnimating(final int addPos, final AppWindowToken atoken) {
if (addPos == 0 || addPos == mAnimatingAppTokens.size()) {
// It was inserted into the beginning or end of mAppTokens. Honor that.
- mAnimatingAppTokens.add(addPos, wtoken);
+ mAnimatingAppTokens.add(addPos, atoken);
return;
}
// Find the item immediately above the mAppTokens insertion point and put the token
// immediately below that one in mAnimatingAppTokens.
final AppWindowToken aboveAnchor = mAppTokens.get(addPos + 1);
- mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), wtoken);
+ mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), atoken);
}
@Override
@@ -3567,30 +3724,31 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized(mWindowMap) {
- AppWindowToken wtoken = findAppWindowToken(token.asBinder());
- if (wtoken != null) {
+ AppWindowToken atoken = findAppWindowToken(token.asBinder());
+ if (atoken != null) {
Slog.w(TAG, "Attempted to add existing app token: " + token);
return;
}
- wtoken = new AppWindowToken(this, token);
- wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
- wtoken.groupId = groupId;
- wtoken.appFullscreen = fullscreen;
- wtoken.requestedOrientation = requestedOrientation;
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + wtoken
+ atoken = new AppWindowToken(this, token);
+ atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
+ atoken.groupId = groupId;
+ atoken.appFullscreen = fullscreen;
+ atoken.requestedOrientation = requestedOrientation;
+ if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+ " at " + addPos);
- mAppTokens.add(addPos, wtoken);
- addAppTokenToAnimating(addPos, wtoken);
- mTokenMap.put(token.asBinder(), wtoken);
+ mAppTokens.add(addPos, atoken);
+ addAppTokenToAnimating(addPos, atoken);
+ mTokenMap.put(token.asBinder(), atoken);
// Application tokens start out hidden.
- wtoken.hidden = true;
- wtoken.hiddenRequested = true;
+ atoken.hidden = true;
+ atoken.hiddenRequested = true;
//dump();
}
}
+ @Override
public void setAppGroupId(IBinder token, int groupId) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppGroupId()")) {
@@ -3598,12 +3756,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized(mWindowMap) {
- AppWindowToken wtoken = findAppWindowToken(token);
- if (wtoken == null) {
+ AppWindowToken atoken = findAppWindowToken(token);
+ if (atoken == null) {
Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token);
return;
}
- wtoken.groupId = groupId;
+ atoken.groupId = groupId;
}
}
@@ -3617,9 +3775,11 @@ public class WindowManagerService extends IWindowManager.Stub
return mLastWindowForcedOrientation;
}
- int pos = mWindows.size() - 1;
+ // TODO(multidisplay): Change to the correct display.
+ final WindowList windows = getDefaultWindowListLocked();
+ 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 +3793,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);
}
@@ -3647,20 +3807,20 @@ public class WindowManagerService extends IWindowManager.Stub
boolean haveGroup = false;
boolean lastFullscreen = false;
for (int pos = mAppTokens.size() - 1; pos >= 0; pos--) {
- AppWindowToken wtoken = mAppTokens.get(pos);
+ AppWindowToken atoken = mAppTokens.get(pos);
- if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken);
+ if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken);
// if we're about to tear down this window and not seek for
// the behind activity, don't use it for orientation
if (!findingBehind
- && (!wtoken.hidden && wtoken.hiddenRequested)) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+ && (!atoken.hidden && atoken.hiddenRequested)) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ " -- going to hide");
continue;
}
- if (haveGroup == true && curGroup != wtoken.groupId) {
+ if (haveGroup == true && curGroup != atoken.groupId) {
// If we have hit a new application group, and the bottom
// of the previous group didn't explicitly say to use
// the orientation behind it, and the last app was
@@ -3668,33 +3828,33 @@ public class WindowManagerService extends IWindowManager.Stub
// user's orientation.
if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
&& lastFullscreen) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
+ " -- end of group, return " + lastOrientation);
return lastOrientation;
}
}
// We ignore any hidden applications on the top.
- if (wtoken.hiddenRequested || wtoken.willBeHidden) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+ if (atoken.hiddenRequested || atoken.willBeHidden) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
+ " -- hidden on top");
continue;
}
if (!haveGroup) {
haveGroup = true;
- curGroup = wtoken.groupId;
- lastOrientation = wtoken.requestedOrientation;
- }
+ curGroup = atoken.groupId;
+ lastOrientation = atoken.requestedOrientation;
+ }
- int or = wtoken.requestedOrientation;
+ int or = atoken.requestedOrientation;
// If this application is fullscreen, and didn't explicitly say
// to use the orientation behind it, then just take whatever
// orientation it has and ignores whatever is under it.
- lastFullscreen = wtoken.appFullscreen;
+ lastFullscreen = atoken.appFullscreen;
if (lastFullscreen
&& or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
+ " -- full screen, return " + or);
return or;
}
@@ -3702,7 +3862,7 @@ public class WindowManagerService extends IWindowManager.Stub
// then use it.
if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
&& or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
+ " -- explicitly set, return " + or);
return or;
}
@@ -3712,6 +3872,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 +3882,7 @@ public class WindowManagerService extends IWindowManager.Stub
Configuration config = null;
long ident = Binder.clearCallingIdentity();
-
+
synchronized(mWindowMap) {
config = updateOrientationFromAppTokensLocked(currentConfig,
freezeThisOneIfNeeded);
@@ -3737,10 +3898,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (updateOrientationFromAppTokensLocked(false)) {
if (freezeThisOneIfNeeded != null) {
- AppWindowToken wtoken = findAppWindowToken(
+ AppWindowToken atoken = findAppWindowToken(
freezeThisOneIfNeeded);
- if (wtoken != null) {
- startAppFreezingScreenLocked(wtoken,
+ if (atoken != null) {
+ startAppFreezingScreenLocked(atoken,
ActivityInfo.CONFIG_ORIENTATION);
}
}
@@ -3756,13 +3917,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
- mLayoutNeeded = true;
- startFreezingDisplayLocked(false);
+ getDefaultDisplayContentLocked().layoutNeeded = true;
+ startFreezingDisplayLocked(false, 0, 0);
config = new Configuration(mTempConfiguration);
}
}
}
-
+
return config;
}
@@ -3773,7 +3934,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 +3971,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 +3984,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()")) {
@@ -3830,16 +3993,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized(mWindowMap) {
- AppWindowToken wtoken = findAppWindowToken(token.asBinder());
- if (wtoken == null) {
+ AppWindowToken atoken = findAppWindowToken(token.asBinder());
+ if (atoken == null) {
Slog.w(TAG, "Attempted to set orientation of non-existing app token: " + token);
return;
}
- wtoken.requestedOrientation = requestedOrientation;
+ atoken.requestedOrientation = requestedOrientation;
}
}
+ @Override
public int getAppOrientation(IApplicationToken token) {
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3851,6 +4015,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void setFocusedApp(IBinder token, boolean moveFocusNow) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setFocusedApp()")) {
@@ -3888,6 +4053,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()")) {
@@ -3926,6 +4092,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public int getPendingAppTransition() {
return mNextAppTransition;
}
@@ -3936,6 +4103,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void overridePendingAppTransition(String packageName,
int enterAnim, int exitAnim, IRemoteCallback startedCallback) {
synchronized(mWindowMap) {
@@ -3953,6 +4121,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight) {
synchronized(mWindowMap) {
@@ -3970,15 +4139,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
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);
@@ -3989,6 +4159,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void executeAppTransition() {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"executeAppTransition()")) {
@@ -4075,7 +4246,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);
@@ -4117,7 +4288,7 @@ public class WindowManagerService extends IWindowManager.Stub
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
true /*updateInputWindows*/);
- mLayoutNeeded = true;
+ getDefaultDisplayContentLocked().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
Binder.restoreCallingIdentity(origId);
return;
@@ -4263,6 +4434,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (applyAnimationLocked(wtoken, lp, transit, visible)) {
delayed = runningAppAnimation = true;
}
+ WindowState window = wtoken.findMainWindow();
+ if (window != null) {
+ scheduleNotifyWindowTranstionIfNeededLocked(window, transit);
+ }
changed = true;
}
@@ -4280,15 +4455,21 @@ public class WindowManagerService extends IWindowManager.Stub
if (!runningAppAnimation) {
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_ENTER, true);
+ scheduleNotifyWindowTranstionIfNeededLocked(win,
+ WindowManagerPolicy.TRANSIT_ENTER);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
} else if (win.isVisibleNow()) {
if (!runningAppAnimation) {
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_EXIT, false);
+ scheduleNotifyWindowTranstionIfNeededLocked(win,
+ WindowManagerPolicy.TRANSIT_EXIT);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
@@ -4310,7 +4491,6 @@ public class WindowManagerService extends IWindowManager.Stub
+ wtoken.hiddenRequested);
if (changed) {
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
if (performLayout) {
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4438,6 +4618,7 @@ public class WindowManagerService extends IWindowManager.Stub
mInnerFields.mOrientationChangeComplete = false;
}
unfrozeWindows = true;
+ w.mDisplayContent.layoutNeeded = true;
}
}
if (force || unfrozeWindows) {
@@ -4447,7 +4628,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
stopFreezingDisplayLocked();
@@ -4472,7 +4652,7 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken.mAppAnimator.freezingScreen = true;
mAppsFreezingScreen++;
if (mAppsFreezingScreen == 1) {
- startFreezingDisplayLocked(false);
+ startFreezingDisplayLocked(false, 0, 0);
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.APP_FREEZE_TIMEOUT),
5000);
@@ -4612,14 +4792,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 +4818,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 +4843,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 +4862,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 +4871,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 +4884,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 +4893,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, win);
+ }
}
return index;
}
@@ -4779,12 +4967,20 @@ 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);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
+ }
if (DEBUG_REORDER) Slog.v(TAG, "Final window list:");
if (DEBUG_REORDER) dumpWindowsLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -4818,19 +5014,28 @@ 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);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
- if (updateFocusAndLayout) {
- mInputMonitor.setUpdateInputWindowsNeededLw();
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ if (updateFocusAndLayout && !updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
- assignLayersLocked();
+ assignLayersLocked(windows);
}
- mLayoutNeeded = true;
+ }
+
+ if (updateFocusAndLayout) {
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+
+ // Note that the above updateFocusedWindowLocked conditional used to sit here.
+
if (!mInLayout) {
performLayoutAndPlaceSurfacesLocked();
}
@@ -4849,23 +5054,33 @@ 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) {
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, token);
+ if (newPos != pos) {
+ displayContent.layoutNeeded = true;
+ }
+ pos = newPos;
+ }
+ }
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
+ assignLayersLocked(windows);
}
}
mInputMonitor.setUpdateInputWindowsNeededLw();
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
- assignLayersLocked();
- }
- mLayoutNeeded = true;
+
+ // Note that the above updateFocusedWindowLocked used to sit here.
+
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -4946,59 +5161,69 @@ 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;
+ @Override
+ public void startFreezingScreen(int exitAnim, int enterAnim) {
+ if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
+ "startFreezingScreen()")) {
+ throw new SecurityException("Requires FREEZE_SCREEN permission");
+ }
+
+ synchronized(mWindowMap) {
+ if (!mClientFreezingScreen) {
+ mClientFreezingScreen = true;
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ startFreezingDisplayLocked(false, exitAnim, enterAnim);
+ mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(H.CLIENT_FREEZE_TIMEOUT),
+ 5000);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
}
- return mAllowDisableKeyguard == ALLOW_DISABLE_YES;
}
+ @Override
+ public void stopFreezingScreen() {
+ if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
+ "stopFreezingScreen()")) {
+ throw new SecurityException("Requires FREEZE_SCREEN permission");
+ }
+
+ synchronized(mWindowMap) {
+ if (mClientFreezingScreen) {
+ mClientFreezingScreen = false;
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ stopFreezingDisplayLocked();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+ }
+
+ @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 +5269,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 +5321,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 +5329,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 +5379,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 +5481,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 = getDefaultWindowListLocked();
+ 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 +5637,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;
@@ -5417,8 +5654,10 @@ public class WindowManagerService extends IWindowManager.Stub
">>> OPEN TRANSACTION showStrictModeViolation");
Surface.openTransaction();
try {
+ // TODO(multi-display): support multiple displays
if (mStrictModeFlash == null) {
- mStrictModeFlash = new StrictModeFlash(mDisplay, mFxSession);
+ mStrictModeFlash = new StrictModeFlash(
+ getDefaultDisplayContentLocked().getDisplay(), mFxSession);
}
mStrictModeFlash.setVisibility(on);
} finally {
@@ -5438,10 +5677,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 +5699,10 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
long ident = Binder.clearCallingIdentity();
- dw = mCurDisplayWidth;
- dh = mCurDisplayHeight;
+ final DisplayContent displayContent = getDisplayContentLocked(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 +5716,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 +5770,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
// The screenshot API does not apply the current screen rotation.
- rot = mDisplay.getRotation();
+ rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
int fw = frame.width();
int fh = frame.height();
@@ -5562,10 +5805,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);
@@ -5675,7 +5919,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
changed = updateRotationUncheckedLocked(false);
if (!changed || forceRelayout) {
- mLayoutNeeded = true;
+ getDefaultDisplayContentLocked().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -5687,6 +5931,7 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
}
+ // TODO(multidisplay): Rotate any display?
/**
* Updates the current rotation.
*
@@ -5701,8 +5946,9 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
- if (mAnimator.mScreenRotationAnimation != null &&
- mAnimator.mScreenRotationAnimation.isAnimating()) {
+ ScreenRotationAnimation screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
// Rotation updates cannot be performed while the previous rotation change
// animation is still in progress. Skip this update. We will try updating
// again after the animation is finished and the display is unfrozen.
@@ -5752,60 +5998,70 @@ public class WindowManagerService extends IWindowManager.Stub
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000);
mWaitingForConfig = true;
- mLayoutNeeded = true;
- startFreezingDisplayLocked(inTransaction);
- mInputManager.setDisplayOrientation(0, rotation,
- mDisplay != null ? mDisplay.getExternalRotation() : Surface.ROTATION_0);
+ getDefaultDisplayContentLocked().layoutNeeded = true;
+ startFreezingDisplayLocked(inTransaction, 0, 0);
+ // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
+ screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
// We need to update our screen size information to match the new
// rotation. Note that this is redundant with the later call to
// sendNewConfiguration() that must be called after this function
// returns... however we need to do the screen size part of that
- // before then so we have the correct size to use when initializiation
+ // before then so we have the correct size to use when initializing
// the rotation animation for the new rotation.
computeScreenConfigurationLocked(null);
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (!inTransaction) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION setRotationUnchecked");
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked");
+ }
Surface.openTransaction();
}
try {
// NOTE: We disable the rotation in the emulator because
// it doesn't support hardware OpenGL emulation yet.
- if (CUSTOM_SCREEN_ROTATION && mAnimator.mScreenRotationAnimation != null
- && mAnimator.mScreenRotationAnimation.hasScreenshot()) {
- if (mAnimator.mScreenRotationAnimation.setRotation(rotation, mFxSession,
+ if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
+ && screenRotationAnimation.hasScreenshot()) {
+ if (screenRotationAnimation.setRotationInTransaction(
+ rotation, mFxSession,
MAX_ANIMATION_DURATION, mTransitionAnimationScale,
- mCurDisplayWidth, mCurDisplayHeight)) {
- scheduleAnimationLocked();
+ displayInfo.logicalWidth, displayInfo.logicalHeight)) {
+ updateLayoutToAnimationLocked();
}
}
- Surface.setOrientation(0, rotation);
+
+ mDisplayManagerService.performTraversalInTransactionFromWindowManager();
} finally {
if (!inTransaction) {
Surface.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setRotationUnchecked");
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked");
+ }
}
}
- rebuildBlackFrame();
-
- 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;
mInnerFields.mOrientationChangeComplete = false;
}
}
+
for (int i=mRotationWatchers.size()-1; i>=0; i--) {
try {
mRotationWatchers.get(i).onRotationChanged(rotation);
} catch (RemoteException e) {
}
}
+
+ scheduleNotifyRotationChangedIfNeededLocked(displayContent, rotation);
+
return true;
}
@@ -5864,7 +6120,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 = getDefaultDisplayContentLocked();
+ if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) {
// On devices with a natural orientation of portrait
switch (rotation) {
default:
@@ -5875,7 +6133,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 +6144,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 +6258,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 +6274,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 +6300,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
@@ -6180,6 +6442,110 @@ public class WindowManagerService extends IWindowManager.Stub
return success;
}
+ public void addDisplayContentChangeListener(int displayId,
+ IDisplayContentChangeListener listener) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "addDisplayContentChangeListener()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission");
+ }
+ synchronized(mWindowMap) {
+ DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent.mDisplayContentChangeListeners == null) {
+ displayContent.mDisplayContentChangeListeners =
+ new RemoteCallbackList<IDisplayContentChangeListener>();
+ displayContent.mDisplayContentChangeListeners.register(listener);
+ }
+ }
+ }
+
+ public void removeDisplayContentChangeListener(int displayId,
+ IDisplayContentChangeListener listener) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "removeDisplayContentChangeListener()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission");
+ }
+ synchronized(mWindowMap) {
+ DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent.mDisplayContentChangeListeners != null) {
+ displayContent.mDisplayContentChangeListeners.unregister(listener);
+ if (displayContent.mDisplayContentChangeListeners
+ .getRegisteredCallbackCount() == 0) {
+ displayContent.mDisplayContentChangeListeners = null;
+ }
+ }
+ }
+ }
+
+ void scheduleNotifyWindowTranstionIfNeededLocked(WindowState window, int transition) {
+ DisplayContent displayContent = window.mDisplayContent;
+ if (displayContent.mDisplayContentChangeListeners != null) {
+ WindowInfo info = getWindowInfoForWindowStateLocked(window);
+ mH.obtainMessage(H.NOTIFY_WINDOW_TRANSITION, transition, 0, info).sendToTarget();
+ }
+ }
+
+ private void handleNotifyWindowTranstion(int transition, WindowInfo info) {
+ RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContentLocked(info.displayId);
+ if (displayContent == null) {
+ return;
+ }
+ callbacks = displayContent.mDisplayContentChangeListeners;
+ if (callbacks == null) {
+ return;
+ }
+ }
+ final int callbackCount = callbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < callbackCount; i++) {
+ try {
+ callbacks.getBroadcastItem(i).onWindowTransition(info.displayId,
+ transition, info);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ callbacks.finishBroadcast();
+ }
+ }
+
+ private void scheduleNotifyRotationChangedIfNeededLocked(DisplayContent displayContent,
+ int rotation) {
+ if (displayContent.mDisplayContentChangeListeners != null
+ && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) {
+ mH.obtainMessage(H.NOTIFY_ROTATION_CHANGED, displayContent.getDisplayId(),
+ rotation).sendToTarget();
+ }
+ }
+
+ private void handleNotifyRotationChanged(int displayId, int rotation) {
+ RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent == null) {
+ return;
+ }
+ callbacks = displayContent.mDisplayContentChangeListeners;
+ if (callbacks == null) {
+ return;
+ }
+ }
+ try {
+ final int watcherCount = callbacks.beginBroadcast();
+ for (int i = 0; i < watcherCount; i++) {
+ try {
+ callbacks.getBroadcastItem(i).onRotationChanged(rotation);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ callbacks.finishBroadcast();
+ }
+ }
+
public void addWindowChangeListener(WindowChangeListener listener) {
synchronized(mWindowMap) {
mWindowChangeListeners.add(listener);
@@ -6224,15 +6590,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,25 +6639,27 @@ 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) {
+ // TODO: Multidisplay: for now only use with default display.
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;
}
}
private int reduceConfigLayout(int curLayout, int rotation, float density,
int dw, int dh) {
+ // TODO: Multidisplay: for now only use with default display.
// Get the app screen size at this rotation.
int w = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation);
int h = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation);
@@ -6370,8 +6737,10 @@ 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) {
+ // TODO: Multidisplay: for now only use with default display.
+
// 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,26 +6753,28 @@ 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.screenLayout = sl;
+ outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
+ outConfig.screenLayout =
+ sl|(outConfig.screenLayout&Configuration.SCREENLAYOUT_LAYOUTDIR_MASK);
}
private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm,
int dw, int dh) {
+ // TODO: Multidisplay: for now only use with default display.
dm.noncompatWidthPixels = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation);
dm.noncompatHeightPixels = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation);
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
@@ -6415,9 +6786,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
private int computeCompatSmallestWidth(boolean rotated, DisplayMetrics dm, int dw, int dh) {
+ // TODO: Multidisplay: for now only use with default display.
mTmpDisplayMetrics.setTo(dm);
- dm = mTmpDisplayMetrics;
- int unrotDw, unrotDh;
+ final DisplayMetrics tmpDm = mTmpDisplayMetrics;
+ final int unrotDw, unrotDh;
if (rotated) {
unrotDw = dh;
unrotDh = dw;
@@ -6425,79 +6797,75 @@ public class WindowManagerService extends IWindowManager.Stub
unrotDw = dw;
unrotDh = dh;
}
- int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, dm, unrotDw, unrotDh);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, dm, unrotDh, unrotDw);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, dm, unrotDw, unrotDh);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, dm, unrotDh, unrotDw);
+ int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, tmpDm, unrotDw, unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, tmpDm, unrotDh, unrotDw);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, tmpDm, unrotDw, unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, tmpDm, unrotDh, unrotDw);
return sw;
}
boolean computeScreenConfigurationLocked(Configuration config) {
- if (mDisplay == null) {
+ if (!mDisplayReady) {
return false;
}
+ // TODO(multidisplay): For now, apply Configuration to main screen only.
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+
// 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) {
- orientation = Configuration.ORIENTATION_PORTRAIT;
- } else if (dw > dh) {
- orientation = Configuration.ORIENTATION_LANDSCAPE;
- }
- config.orientation = orientation;
+ config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
+ Configuration.ORIENTATION_LANDSCAPE;
}
- // 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.setDisplayInfoOverrideFromWindowManager(
+ 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 +6874,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,8 +7007,12 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
try {
if (mDragState == null) {
- Surface surface = new Surface(session, callerPid, "drag surface", 0,
+ // TODO(multi-display): support other displays
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final Display display = displayContent.getDisplay();
+ Surface surface = new Surface(session, "drag surface",
width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ surface.setLayerStack(display.getLayerStack());
if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG "
+ surface + ": CREATE");
outSurface.copyFrom(surface);
@@ -6720,8 +7093,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,18 +7107,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // TODO: This is a workaround - remove when 6623031 is fixed.
- public boolean getWindowFrame(IBinder token, Rect outBounds) {
- synchronized (mWindowMap) {
- WindowState windowState = mWindowMap.get(token);
- if (windowState != null) {
- outBounds.set(windowState.getFrameLw());
- return true;
- }
- }
- return false;
- }
-
private WindowState getFocusedWindow() {
synchronized (mWindowMap) {
return getFocusedWindowLocked();
@@ -6790,45 +7154,53 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void displayReady() {
+ displayReady(Display.DEFAULT_DISPLAY);
+
synchronized(mWindowMap) {
- if (mDisplay != null) {
- throw new IllegalStateException("Display already initialized");
- }
- WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- mDisplay = wm.getDefaultDisplay();
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final Display display = displayContent.getDisplay();
+ readForcedDisplaySizeAndDensityLocked(displayContent);
+
+ mDisplayReady = true;
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);
+
+ final DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
+ mAnimator.setDisplayDimensions(
+ displayInfo.logicalWidth, displayInfo.logicalHeight,
+ displayInfo.appWidth, displayInfo.appHeight);
+
+ mPolicy.setInitialDisplaySize(displayContent.getDisplay(),
+ 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 = getDisplayContentLocked(displayId);
+ final DisplayInfo displayInfo;
+ mAnimator.addDisplayLocked(displayId);
+ synchronized(displayContent.mDisplaySizeLock) {
+ // Bootstrap the default logical display from the display manager.
+ displayInfo = displayContent.getDisplayInfo();
+ DisplayInfo newDisplayInfo = mDisplayManagerService.getDisplayInfo(displayId);
+ if (newDisplayInfo != null) {
+ displayInfo.copyFrom(newDisplayInfo);
+ }
+ 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 +7208,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 +7235,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 +7248,22 @@ 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 NOTIFY_ROTATION_CHANGED = 28;
+ public static final int NOTIFY_WINDOW_TRANSITION = 29;
+ public static final int NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 30;
+
+ public static final int DO_DISPLAY_ADDED = 31;
+ public static final int DO_DISPLAY_REMOVED = 32;
+ public static final int DO_DISPLAY_CHANGED = 33;
+
+ public static final int CLIENT_FREEZE_TIMEOUT = 34;
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 +7498,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 = getDefaultWindowListLocked();
+ 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 +7516,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 +7542,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();
@@ -7232,6 +7587,16 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
+ case CLIENT_FREEZE_TIMEOUT: {
+ synchronized (mWindowMap) {
+ if (mClientFreezingScreen) {
+ mClientFreezingScreen = false;
+ stopFreezingDisplayLocked();
+ }
+ }
+ break;
+ }
+
case SEND_NEW_CONFIGURATION: {
removeMessages(SEND_NEW_CONFIGURATION);
sendNewConfiguration();
@@ -7306,42 +7671,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,33 +7696,58 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case SET_WALLPAPER_OFFSET: {
- final WindowStateAnimator winAnimator = (WindowStateAnimator) msg.obj;
- winAnimator.setWallpaperOffset(msg.arg1, msg.arg2);
+ case CLEAR_PENDING_ACTIONS: {
+ mAnimator.clearPendingActions();
+ break;
+ }
- scheduleAnimationLocked();
+ case DO_ANIMATION_CALLBACK: {
+ try {
+ ((IRemoteCallback)msg.obj).sendResult(null);
+ } catch (RemoteException e) {
+ }
break;
}
- case SET_DIM_PARAMETERS: {
- mAnimator.mDimParams = (DimAnimator.Parameters) msg.obj;
+ case NOTIFY_ROTATION_CHANGED: {
+ final int displayId = msg.arg1;
+ final int rotation = msg.arg2;
+ handleNotifyRotationChanged(displayId, rotation);
+ break;
+ }
- scheduleAnimationLocked();
+ case NOTIFY_WINDOW_TRANSITION: {
+ final int transition = msg.arg1;
+ WindowInfo info = (WindowInfo) msg.obj;
+ handleNotifyWindowTranstion(transition, info);
break;
}
- case CLEAR_PENDING_ACTIONS: {
- mAnimator.clearPendingActions();
+ case NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
+ final int displayId = msg.arg1;
+ final boolean immediate = (msg.arg2 == 1);
+ Rect rectangle = (Rect) msg.obj;
+ handleNotifyRectangleOnScreenRequested(displayId, rectangle, immediate);
break;
}
- case DO_ANIMATION_CALLBACK: {
- try {
- ((IRemoteCallback)msg.obj).sendResult(null);
- } catch (RemoteException e) {
+ case DO_DISPLAY_ADDED:
+ synchronized (mWindowMap) {
+ handleDisplayAddedLocked(msg.arg1);
+ }
+ break;
+
+ case DO_DISPLAY_REMOVED:
+ synchronized (mWindowMap) {
+ handleDisplayRemovedLocked(msg.arg1);
+ }
+ break;
+
+ case DO_DISPLAY_CHANGED:
+ synchronized (mWindowMap) {
+ handleDisplayChangedLocked(msg.arg1);
}
break;
- }
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -7416,13 +7774,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 = getDefaultWindowListLocked().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,143 +7810,148 @@ 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;
- }
}
}
+
+ // 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 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 = getDisplayContentLocked(displayId);
+ synchronized(displayContent.mDisplaySizeLock) {
+ size.x = displayContent.mInitialDisplayWidth;
+ size.y = displayContent.mInitialDisplayHeight;
}
}
- public void getRealDisplaySize(Point size) {
- synchronized(mDisplaySizeLock) {
- size.x = mCurDisplayWidth;
- size.y = mCurDisplayHeight;
+ public void setForcedDisplaySize(int displayId, int width, int height) {
+ synchronized(mWindowMap) {
+ // Set some sort of reasonable bounds on the size of the display that we
+ // will try to emulate.
+ final int MIN_WIDTH = 200;
+ final int MIN_HEIGHT = 200;
+ final int MAX_SCALE = 2;
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+
+ width = Math.min(Math.max(width, MIN_WIDTH),
+ displayContent.mInitialDisplayWidth * MAX_SCALE);
+ height = Math.min(Math.max(height, MIN_HEIGHT),
+ displayContent.mInitialDisplayHeight * MAX_SCALE);
+ setForcedDisplaySizeLocked(displayContent, width, height);
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.DISPLAY_SIZE_FORCED, width + "," + height);
+ }
+ }
+
+ private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
+ final String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.DISPLAY_SIZE_FORCED);
+ 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) {
+ Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height);
+ displayContent.mBaseDisplayWidth = width;
+ displayContent.mBaseDisplayHeight = height;
+ }
+ }
+ } catch (NumberFormatException ex) {
+ }
+ }
}
- }
-
- public void getInitialDisplaySize(Point size) {
- synchronized(mDisplaySizeLock) {
- size.x = mInitialDisplayWidth;
- size.y = mInitialDisplayHeight;
+ final String densityStr = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.DISPLAY_DENSITY_FORCED);
+ if (densityStr != null && densityStr.length() > 0) {
+ int density;
+ try {
+ density = Integer.parseInt(densityStr);
+ synchronized(displayContent.mDisplaySizeLock) {
+ if (displayContent.mBaseDisplayDensity != density) {
+ Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density);
+ displayContent.mBaseDisplayDensity = density;
+ }
+ }
+ } catch (NumberFormatException ex) {
+ }
}
}
- public int getMaximumSizeDimension() {
- synchronized(mDisplaySizeLock) {
- // Do this based on the raw screen size, until we are smarter.
- return mBaseDisplayWidth > mBaseDisplayHeight
- ? mBaseDisplayWidth : mBaseDisplayHeight;
+ private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) {
+ Slog.i(TAG, "Using new display size: " + width + "x" + height);
+
+ synchronized(displayContent.mDisplaySizeLock) {
+ displayContent.mBaseDisplayWidth = width;
+ displayContent.mBaseDisplayHeight = height;
}
+ reconfigureDisplayLocked(displayContent);
}
- public void getCurrentSizeRange(Point smallestSize, Point largestSize) {
- synchronized(mDisplaySizeLock) {
- smallestSize.x = mSmallestDisplayWidth;
- smallestSize.y = mSmallestDisplayHeight;
- largestSize.x = mLargestDisplayWidth;
- largestSize.y = mLargestDisplayHeight;
+ public void clearForcedDisplaySize(int displayId) {
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
+ displayContent.mInitialDisplayHeight);
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.DISPLAY_SIZE_FORCED, "");
}
}
- public void setForcedDisplaySize(int longDimen, int shortDimen) {
+ public void setForcedDisplayDensity(int displayId, int density) {
synchronized(mWindowMap) {
- int width, height;
- if (mInitialDisplayWidth < mInitialDisplayHeight) {
- width = shortDimen < mInitialDisplayWidth
- ? shortDimen : mInitialDisplayWidth;
- height = longDimen < mInitialDisplayHeight
- ? longDimen : mInitialDisplayHeight;
- } else {
- width = longDimen < mInitialDisplayWidth
- ? longDimen : mInitialDisplayWidth;
- height = shortDimen < mInitialDisplayHeight
- ? shortDimen : mInitialDisplayHeight;
- }
- setForcedDisplaySizeLocked(width, height);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DISPLAY_SIZE_FORCED, width + "," + height);
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ setForcedDisplayDensityLocked(displayContent, density);
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density));
}
}
- private void rebuildBlackFrame() {
- if (mBlackFrame != null) {
- mBlackFrame.kill();
- mBlackFrame = null;
- }
- if (mBaseDisplayWidth < mInitialDisplayWidth
- || mBaseDisplayHeight < mInitialDisplayHeight) {
- int initW, initH, baseW, baseH;
- final boolean rotated = (mRotation == Surface.ROTATION_90
- || mRotation == Surface.ROTATION_270);
- if (rotated) {
- initW = mInitialDisplayHeight;
- initH = mInitialDisplayWidth;
- baseW = mBaseDisplayHeight;
- baseH = mBaseDisplayWidth;
- } else {
- initW = mInitialDisplayWidth;
- initH = mInitialDisplayHeight;
- baseW = mBaseDisplayWidth;
- baseH = 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);
- } catch (Surface.OutOfResourcesException e) {
- }
+ private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) {
+ Slog.i(TAG, "Using new display density: " + density);
+
+ synchronized(displayContent.mDisplaySizeLock) {
+ displayContent.mBaseDisplayDensity = density;
}
+ reconfigureDisplayLocked(displayContent);
}
- private void readForcedDisplaySizeLocked() {
- final String str = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.DISPLAY_SIZE_FORCED);
- if (str == null || str.length() == 0) {
- return;
- }
- final int pos = str.indexOf(',');
- if (pos <= 0 || str.lastIndexOf(',') != pos) {
- return;
- }
- int width, height;
- try {
- width = Integer.parseInt(str.substring(0, pos));
- height = Integer.parseInt(str.substring(pos+1));
- } catch (NumberFormatException ex) {
- return;
+ public void clearForcedDisplayDensity(int displayId) {
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity);
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.DISPLAY_DENSITY_FORCED, "");
}
- setForcedDisplaySizeLocked(width, height);
}
- private void setForcedDisplaySizeLocked(int width, int height) {
- Slog.i(TAG, "Using new display size: " + width + "x" + height);
-
- synchronized(mDisplaySizeLock) {
- mBaseDisplayWidth = width;
- mBaseDisplayHeight = height;
- }
- mPolicy.setInitialDisplaySize(mDisplay, mBaseDisplayWidth, mBaseDisplayHeight);
+ private void reconfigureDisplayLocked(DisplayContent displayContent) {
+ // TODO: Multidisplay: for now only use with default display.
+ mPolicy.setInitialDisplaySize(displayContent.getDisplay(),
+ displayContent.mBaseDisplayWidth,
+ displayContent.mBaseDisplayHeight,
+ displayContent.mBaseDisplayDensity);
- mLayoutNeeded = true;
+ displayContent.layoutNeeded = true;
boolean configChanged = updateOrientationFromAppTokensLocked(false);
mTempConfiguration.setToDefaults();
@@ -7601,23 +7964,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (configChanged) {
mWaitingForConfig = true;
- startFreezingDisplayLocked(false);
+ startFreezingDisplayLocked(false, 0, 0);
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
- rebuildBlackFrame();
-
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 +8013,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 +8033,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 +8044,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 +8064,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 +8096,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 +8109,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;
@@ -7779,9 +8142,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (winAnimator.mAnimLayer != oldLayer) {
layerChanged = true;
}
- if (layerChanged && mAnimator.isDimming(winAnimator)) {
+ if (layerChanged && mAnimator.isDimmingLocked(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 +8171,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (mDisplay == null) {
+ if (!mDisplayReady) {
// Not yet initialized, nothing to do.
return;
}
@@ -7839,35 +8202,13 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception while force removing for memory", e);
}
-
+
try {
performLayoutAndPlaceSurfacesLockedInner(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);
- }
-
- 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 (needsLayout()) {
if (++mLayoutRepeatCount < 6) {
requestTraversalLocked();
} else {
@@ -7890,32 +8231,41 @@ public class WindowManagerService extends IWindowManager.Stub
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
- private final void performLayoutLockedInner(boolean initial, boolean updateInputWindows) {
- if (!mLayoutNeeded) {
+ private final void performLayoutLockedInner(final DisplayContent displayContent,
+ boolean initial, boolean updateInputWindows) {
+ if (!displayContent.layoutNeeded) {
return;
}
-
- mLayoutNeeded = false;
-
- final int dw = mCurDisplayWidth;
- final int dh = mCurDisplayHeight;
+ displayContent.layoutNeeded = false;
+ WindowList windows = displayContent.getWindowList();
+ boolean isDefaultDisplay = displayContent.isDefaultDisplay;
+
+ 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) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: needed="
- + mLayoutNeeded + " dw=" + dw + " dh=" + dh);
+ + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
+ }
+
+ WindowStateAnimator universeBackground = null;
+
+ mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation);
+ if (isDefaultDisplay) {
+ // Not needed on non-default displays.
+ mSystemDecorLayer = mPolicy.getSystemDecorRectLw(mSystemDecorRect);
+ mScreenRect.set(0, 0, dw, dh);
}
-
- mPolicy.beginLayoutLw(dw, dh, mRotation);
- mSystemDecorLayer = mPolicy.getSystemDecorRectLw(mSystemDecorRect);
int seq = mLayoutSeq+1;
if (seq < 0) seq = 0;
@@ -7925,7 +8275,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
@@ -7950,13 +8300,14 @@ public class WindowManagerService extends IWindowManager.Stub
+ (atoken != null && atoken.hiddenRequested)
+ " mAttachedHidden=" + win.mAttachedHidden);
}
-
+
// If this view is GONE, then skip it -- keep the current
// frame, and let the caller know so they can ignore it
// 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 +8325,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 +8342,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 +8403,10 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- *
+ * @param windows List of windows on default display.
* @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 +8561,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++) {
@@ -8248,9 +8617,14 @@ public class WindowManagerService extends IWindowManager.Stub
Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(),
mNextAppTransitionThumbnail.getHeight());
try {
- Surface surface = new Surface(mFxSession, Process.myPid(),
- "thumbnail anim", 0, dirty.width(), dirty.height(),
+ // TODO(multi-display): support other displays
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final Display display = displayContent.getDisplay();
+ Surface surface = new Surface(mFxSession,
+ "thumbnail anim",
+ dirty.width(), dirty.height(),
PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ surface.setLayerStack(display.getLayerStack());
topOpeningApp.mAppAnimator.thumbnail = surface;
if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL "
+ surface + ": CREATE");
@@ -8262,7 +8636,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);
@@ -8286,14 +8660,16 @@ public class WindowManagerService extends IWindowManager.Stub
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
- changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
| WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
- mLayoutNeeded = true;
- if (!moveInputMethodWindowsIfNeededLocked(true)) {
- assignLayersLocked();
+ getDefaultDisplayContentLocked().layoutNeeded = true;
+
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ if (windows == getDefaultWindowListLocked()
+ && !moveInputMethodWindowsIfNeededLocked(true)) {
+ assignLayersLocked(windows);
}
- updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
- false /*updateInputWindows*/);
+ updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, false /*updateInputWindows*/);
mFocusMayChange = false;
}
@@ -8302,7 +8678,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() {
@@ -8473,17 +8848,18 @@ public class WindowManagerService extends IWindowManager.Stub
//Slog.i(TAG, "DIM BEHIND: " + w);
mInnerFields.mDimming = true;
final WindowStateAnimator winAnimator = w.mWinAnimator;
- if (!mAnimator.isDimming(winAnimator)) {
+ if (!mAnimator.isDimmingLocked(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);
+ startDimmingLocked(
+ winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount, width, height);
}
}
}
@@ -8510,22 +8886,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
// "Something has changed! Let's make it correct now."
- private final void performLayoutAndPlaceSurfacesLockedInner(
- boolean recoveringMemory) {
+ private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by "
+ Debug.getCallers(3));
}
- if (mDisplay == null) {
- Slog.i(TAG, "skipping performLayoutAndPlaceSurfacesLockedInner with no mDisplay");
- return;
- }
final long currentTime = SystemClock.uptimeMillis();
- final int dw = mCurDisplayWidth;
- final int dh = mCurDisplayHeight;
- final int innerDw = mAppDisplayWidth;
- final int innerDh = mAppDisplayHeight;
int i;
@@ -8550,193 +8917,238 @@ public class WindowManagerService extends IWindowManager.Stub
mInnerFields.mButtonBrightness = -1;
mTransactionSequence++;
+ final DisplayContent defaultDisplay = getDefaultDisplayContentLocked();
+ final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
+ final int defaultDw = defaultInfo.logicalWidth;
+ final int defaultDh = defaultInfo.logicalHeight;
+
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
-
Surface.openTransaction();
-
- if (mWatermark != null) {
- mWatermark.positionSurface(dw, dh);
- }
- if (mStrictModeFlash != null) {
- mStrictModeFlash.positionSurface(dw, dh);
- }
-
try {
- int repeats = 0;
- do {
- repeats++;
- if (repeats > 6) {
- Slog.w(TAG, "Animation repeat aborted after too many iterations");
- mLayoutNeeded = false;
- break;
- }
+ if (mWatermark != null) {
+ mWatermark.positionSurface(defaultDw, defaultDh);
+ }
+ if (mStrictModeFlash != null) {
+ mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+ }
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
- mPendingLayoutChanges);
+ // Give the display manager a chance to adjust properties
+ // like display rotation if it needs to.
+ mDisplayManagerService.performTraversalInTransactionFromWindowManager();
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
- assignLayersLocked();
- mLayoutNeeded = true;
+ boolean focusDisplayed = false;
+ boolean updateAllDrawn = false;
+
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ WindowList windows = displayContent.getWindowList();
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int displayId = displayContent.getDisplayId();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+ final int innerDw = displayInfo.appWidth;
+ final int innerDh = displayInfo.appHeight;
+ final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+
+ int repeats = 0;
+ do {
+ repeats++;
+ if (repeats > 6) {
+ Slog.w(TAG, "Animation repeat aborted after too many iterations");
+ displayContent.layoutNeeded = false;
+ break;
}
- }
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
- if (updateOrientationFromAppTokensLocked(true)) {
- mLayoutNeeded = true;
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
+ displayContent.pendingLayoutChanges);
+
+ if (isDefaultDisplay && ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0)
+ && ((adjustWallpaperWindowsLocked()
+ & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0)) {
+ assignLayersLocked(windows);
+ displayContent.layoutNeeded = true;
}
- }
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- mLayoutNeeded = true;
- }
+ if (isDefaultDisplay && (displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+ if (updateOrientationFromAppTokensLocked(true)) {
+ displayContent.layoutNeeded = true;
+ mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+ }
+ }
- // FIRST LOOP: Perform a layout, if needed.
- if (repeats < 4) {
- performLayoutLockedInner(repeats == 1, false /*updateInputWindows*/);
- } else {
- Slog.w(TAG, "Layout repeat skipped after too many iterations");
- }
+ if ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ displayContent.layoutNeeded = true;
+ }
- // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
- // it is animating.
- mPendingLayoutChanges = 0;
- 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);
- if (w.mHasSurface) {
- mPolicy.animatingWindowLw(w, w.mAttrs);
+ // FIRST LOOP: Perform a layout, if needed.
+ if (repeats < 4) {
+ performLayoutLockedInner(displayContent, repeats == 1,
+ false /*updateInputWindows*/);
+ } else {
+ Slog.w(TAG, "Layout repeat skipped after too many iterations");
}
- }
- mPendingLayoutChanges |= mPolicy.finishAnimationLw();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw",
- mPendingLayoutChanges);
- } while (mPendingLayoutChanges != 0);
- final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
+ // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
+ // it is animating.
+ displayContent.pendingLayoutChanges = 0;
- mInnerFields.mObscured = false;
- mInnerFields.mDimming = false;
- mInnerFields.mSyswin = false;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number "
+ + mLayoutRepeatCount, displayContent.pendingLayoutChanges);
- boolean focusDisplayed = false;
- boolean updateAllDrawn = false;
- final int N = mWindows.size();
- for (i=N-1; i>=0; i--) {
- WindowState w = mWindows.get(i);
+ if (isDefaultDisplay) {
+ mPolicy.beginPostLayoutPolicyLw(dw, dh);
+ for (i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ if (w.mHasSurface) {
+ mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs);
+ }
+ }
+ displayContent.pendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw();
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats(
+ "after finishPostLayoutPolicyLw", displayContent.pendingLayoutChanges);
+ }
+ } while (displayContent.pendingLayoutChanges != 0);
- final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
+ mInnerFields.mObscured = false;
+ mInnerFields.mDimming = false;
+ mInnerFields.mSyswin = false;
- // Update effect.
- w.mObscured = mInnerFields.mObscured;
- if (!mInnerFields.mObscured) {
- handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
- }
+ // Only used if default window
+ final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
- if (obscuredChanged && (mWallpaperTarget == w) && w.isVisibleLw()) {
- // This is the wallpaper target and its obscured state
- // changed... make sure the current wallaper's visibility
- // has been updated accordingly.
- updateWallpaperVisibilityLocked();
- }
+ final int N = windows.size();
+ for (i=N-1; i>=0; i--) {
+ WindowState w = windows.get(i);
- final WindowStateAnimator winAnimator = w.mWinAnimator;
+ final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
- // If the window has moved due to its containing
- // content frame changing, then we'd like to animate
- // it.
- if (w.mHasSurface && w.shouldAnimateMove()) {
- // Frame has moved, containing content frame
- // has also moved, and we're not currently animating...
- // let's do something.
- Animation a = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.window_move_from_decor);
- winAnimator.setAnimation(a);
- winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
- winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
- }
+ // Update effect.
+ w.mObscured = mInnerFields.mObscured;
+ if (!mInnerFields.mObscured) {
+ handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
+ }
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
+ if (isDefaultDisplay && obscuredChanged && (mWallpaperTarget == w)
+ && w.isVisibleLw()) {
+ // This is the wallpaper target and its obscured state
+ // changed... make sure the current wallaper's visibility
+ // has been updated accordingly.
+ updateWallpaperVisibilityLocked();
+ }
- // Moved from updateWindowsAndWallpaperLocked().
- if (w.mHasSurface) {
- // Take care of the window being ready to display.
- if (winAnimator.commitFinishDrawingLocked(currentTime)) {
- if ((w.mAttrs.flags
- & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
- if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
- "First draw done in potential wallpaper target " + w);
- mInnerFields.mWallpaperMayChange = true;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats("updateWindowsAndWallpaperLocked 1",
- mPendingLayoutChanges);
- }
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+ // If the window has moved due to its containing
+ // content frame changing, then we'd like to animate
+ // it.
+ if (w.mHasSurface && w.shouldAnimateMove()) {
+ // Frame has moved, containing content frame
+ // has also moved, and we're not currently animating...
+ // let's do something.
+ Animation a = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ 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) {
}
}
- winAnimator.setSurfaceBoundaries(recoveringMemory);
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+ w.mContentChanged = false;
- final AppWindowToken atoken = w.mAppToken;
- if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
- Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
- + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
- }
- if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
- if (atoken.lastTransactionSequence != mTransactionSequence) {
- atoken.lastTransactionSequence = mTransactionSequence;
- atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
- atoken.startingDisplayed = false;
- }
- if ((w.isOnScreen() || winAnimator.mAttrType
- == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
- && !w.mExiting && !w.mDestroying) {
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) {
- Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimating=" + winAnimator.isAnimating());
- if (!w.isDrawnLw()) {
- Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
- + " pv=" + w.mPolicyVisibility
- + " mDrawState=" + winAnimator.mDrawState
- + " ah=" + w.mAttachedHidden
- + " th=" + atoken.hiddenRequested
- + " a=" + winAnimator.mAnimating);
+ // Moved from updateWindowsAndWallpaperLocked().
+ if (w.mHasSurface) {
+ // Take care of the window being ready to display.
+ final boolean committed =
+ winAnimator.commitFinishDrawingLocked(currentTime);
+ if (isDefaultDisplay && committed) {
+ if ((w.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+ "First draw done in potential wallpaper target " + w);
+ mInnerFields.mWallpaperMayChange = true;
+ displayContent.pendingLayoutChanges |=
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ debugLayoutRepeats(
+ "wallpaper and commitFinishDrawingLocked true",
+ displayContent.pendingLayoutChanges);
}
}
- if (w != atoken.startingWindow) {
- if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
- atoken.numInterestingWindows++;
- if (w.isDrawnLw()) {
- atoken.numDrawnWindows++;
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
- "tokenMayBeDrawn: " + atoken
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
- updateAllDrawn = true;
+ }
+
+ winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
+
+ final AppWindowToken atoken = w.mAppToken;
+ if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
+ Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
+ + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ }
+ if (atoken != null
+ && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
+ if (atoken.lastTransactionSequence != mTransactionSequence) {
+ atoken.lastTransactionSequence = mTransactionSequence;
+ atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+ atoken.startingDisplayed = false;
+ }
+ if ((w.isOnScreen() || winAnimator.mAttrType
+ == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+ && !w.mExiting && !w.mDestroying) {
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+ + ", isAnimating=" + winAnimator.isAnimating());
+ if (!w.isDrawnLw()) {
+ Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
+ + " pv=" + w.mPolicyVisibility
+ + " mDrawState=" + winAnimator.mDrawState
+ + " ah=" + w.mAttachedHidden
+ + " th=" + atoken.hiddenRequested
+ + " a=" + winAnimator.mAnimating);
}
}
- } else if (w.isDrawnLw()) {
- atoken.startingDisplayed = true;
+ if (w != atoken.startingWindow) {
+ if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
+ atoken.numInterestingWindows++;
+ if (w.isDrawnLw()) {
+ atoken.numDrawnWindows++;
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+ "tokenMayBeDrawn: " + atoken
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+ updateAllDrawn = true;
+ }
+ }
+ } else if (w.isDrawnLw()) {
+ atoken.startingDisplayed = true;
+ }
}
}
}
- }
- if (someoneLosingFocus && w == mCurrentFocus && w.isDisplayedLw()) {
- focusDisplayed = true;
+ if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus)
+ && w.isDisplayedLw()) {
+ focusDisplayed = true;
+ }
+
+ updateResizingWindows(w);
}
- updateResizingWindows(w);
+ if (!mInnerFields.mDimming && mAnimator.isDimmingLocked(displayId)) {
+ stopDimmingLocked(displayId);
+ }
}
if (updateAllDrawn) {
@@ -8746,23 +9158,21 @@ public class WindowManagerService extends IWindowManager.Stub
if (focusDisplayed) {
mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
}
-
- if (!mInnerFields.mDimming && mAnimator.isDimming()) {
- mAnimator.stopDimming();
- }
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
Surface.closeTransaction();
}
+ final WindowList defaultWindows = defaultDisplay.getWindowList();
+
// If we are ready to perform an app transition, check through
// all of the app tokens to be shown and see if they are ready
// to go.
if (mAppTransitionReady) {
- mPendingLayoutChanges |= handleAppTransitionReadyLocked();
+ defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mAdjResult = 0;
@@ -8774,22 +9184,22 @@ public class WindowManagerService extends IWindowManager.Stub
// reflects the correct Z-order, but the window list may now
// be out of sync with it. So here we will just rebuild the
// entire app window list. Fun!
- mPendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
+ defaultDisplay.pendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
- if (mInnerFields.mWallpaperForceHidingChanged && mPendingLayoutChanges == 0 &&
- !mAppTransitionReady) {
+ if (mInnerFields.mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
+ && !mAppTransitionReady) {
// At this point, there was a window with a wallpaper that
// was force hiding other windows behind it, but now it
// is going away. This may be simple -- just animate
// away the wallpaper and its window -- or it may be
// hard -- the wallpaper now needs to be shown behind
// something that was hidden.
- mPendingLayoutChanges |= animateAwayWallpaperLocked();
+ defaultDisplay.pendingLayoutChanges |= animateAwayWallpaperLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mWallpaperForceHidingChanged = false;
@@ -8802,26 +9212,27 @@ public class WindowManagerService extends IWindowManager.Stub
if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper layer changed: assigning layers + relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- assignLayersLocked();
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ assignLayersLocked(defaultWindows);
} else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper visibility changed: relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
}
if (mFocusMayChange) {
mFocusMayChange = false;
if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
mInnerFields.mAdjResult = 0;
}
}
- if (mLayoutNeeded) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", mPendingLayoutChanges);
+ if (needsLayout()) {
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded",
+ defaultDisplay.pendingLayoutChanges);
}
if (!mResizingWindows.isEmpty()) {
@@ -8847,9 +9258,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 +9308,7 @@ public class WindowManagerService extends IWindowManager.Stub
mExitingTokens.remove(i);
if (token.windowType == TYPE_WALLPAPER) {
mWallpaperTokens.remove(token);
+ updateLayoutToAnimWallpaperTokens();
}
}
}
@@ -8930,41 +9340,40 @@ public class WindowManagerService extends IWindowManager.Stub
mRelayoutWhileAnimating.clear();
}
- if (wallpaperDestroyed) {
- mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0;
+ if (wallpaperDestroyed && (adjustWallpaperWindowsLocked() != 0)) {
+ getDefaultDisplayContentLocked().layoutNeeded = true;
}
- if (mPendingLayoutChanges != 0) {
- mLayoutNeeded = true;
+
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ DisplayContent displayContent = iterator.next();
+ if (displayContent.pendingLayoutChanges != 0) {
+ displayContent.layoutNeeded = true;
+ }
}
// 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;
}
@@ -8977,24 +9386,49 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded &&
- !mInnerFields.mUpdateRotation) {
+ if (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded
+ && !mInnerFields.mUpdateRotation) {
checkDrawnWindowsLocked();
}
+ final int N = mPendingRemove.size();
+ if (N > 0) {
+ if (mPendingRemoveTmp.length < N) {
+ mPendingRemoveTmp = new WindowState[N+10];
+ }
+ mPendingRemove.toArray(mPendingRemoveTmp);
+ mPendingRemove.clear();
+ DisplayContentList displayList = new DisplayContentList();
+ for (i = 0; i < N; i++) {
+ WindowState w = mPendingRemoveTmp[i];
+ removeWindowInnerLocked(w.mSession, w);
+ if (!displayList.contains(w.mDisplayContent)) {
+ displayList.add(w.mDisplayContent);
+ }
+ }
+
+ for (DisplayContent displayContent : displayList) {
+ assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
- scheduleAnimationLocked();
+ updateLayoutToAnimationLocked();
if (DEBUG_WINDOW_TRACE) {
- Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges="
- + Integer.toHexString(mPendingLayoutChanges) + " mLayoutNeeded=" + mLayoutNeeded
- + " animating=" + mAnimator.mAnimating);
+ Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: animating="
+ + mAnimator.mAnimating);
}
}
+ 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 +9474,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 {
@@ -9056,6 +9494,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
+ public void requestTraversal() {
+ synchronized (mWindowMap) {
+ requestTraversalLocked();
+ }
+ }
+
void requestTraversalLocked() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
@@ -9063,11 +9508,133 @@ 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.
+ SparseArray<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.put(displayContent.getDisplayId(), 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(int displayId, DimAnimator.Parameters params) {
+ synchronized (mLayoutToAnim) {
+ mLayoutToAnim.mDimParams.put(displayId, params);
+ scheduleAnimationLocked();
+ }
+ }
+
+ void startDimmingLocked(final WindowStateAnimator winAnimator, final float target,
+ final int width, final int height) {
+ setAnimDimParams(winAnimator.mWin.getDisplayId(),
+ new DimAnimator.Parameters(winAnimator, width, height, target));
+ }
+
+ void stopDimmingLocked(int displayId) {
+ setAnimDimParams(displayId, null);
+ }
+
+ private boolean needsLayout() {
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().layoutNeeded) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ 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;
+ }
+
+ SparseIntArray pendingLayouts = animToLayout.mPendingLayoutChanges;
+ final int count = pendingLayouts.size();
+ if (count > 0) {
+ doRequest = true;
+ }
+ for (int i = 0; i < count; ++i) {
+ final DisplayContent displayContent =
+ getDisplayContentLocked(pendingLayouts.keyAt(i));
+ displayContent.pendingLayoutChanges |= pendingLayouts.valueAt(i);
+ }
+
+ mWindowDetachedWallpaper = animToLayout.mWindowDetachedWallpaper;
}
+ return doRequest;
}
boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation,
@@ -9088,10 +9655,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 +9674,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 +9692,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,28 +9758,31 @@ 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 = getDefaultDisplayContentLocked();
+
final WindowState imWindow = mInputMethodWindow;
if (newFocus != imWindow && oldFocus != imWindow) {
if (moveInputMethodWindowsIfNeededLocked(
mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
- mLayoutNeeded = true;
+ getDefaultDisplayContentLocked().layoutNeeded = 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());
}
}
- if ((focusChanged&WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
// The change in focus caused us to need to do a layout. Okay.
- mLayoutNeeded = true;
+ getDefaultDisplayContentLocked().layoutNeeded = true;
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- performLayoutLockedInner(true /*initial*/, updateInputWindows);
+ performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
}
}
@@ -9232,12 +9806,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 = getDefaultWindowListLocked();
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ win = windows.get(i);
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Looking for focus: " + i
@@ -9295,12 +9876,13 @@ public class WindowManagerService extends IWindowManager.Stub
return result;
}
- private void startFreezingDisplayLocked(boolean inTransaction) {
+ private void startFreezingDisplayLocked(boolean inTransaction,
+ int exitAnim, int enterAnim) {
if (mDisplayFrozen) {
return;
}
- if (mDisplay == null || !mPolicy.isScreenOnFully()) {
+ if (!mDisplayReady || !mPolicy.isScreenOnFully()) {
// No need to freeze the screen before the system is ready or if
// the screen is off.
return;
@@ -9326,20 +9908,22 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (CUSTOM_SCREEN_ROTATION) {
- if (mAnimator.mScreenRotationAnimation != null) {
- mAnimator.mScreenRotationAnimation.kill();
- mAnimator.mScreenRotationAnimation = null;
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final int displayId = displayContent.getDisplayId();
+ ScreenRotationAnimation screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(displayId);
+ if (screenRotationAnimation != null) {
+ screenRotationAnimation.kill();
}
- mAnimator.mScreenRotationAnimation = new ScreenRotationAnimation(mContext,
- mFxSession, inTransaction, mCurDisplayWidth, mCurDisplayHeight,
- mDisplay.getRotation());
-
- if (!mAnimator.mScreenRotationAnimation.hasScreenshot()) {
- Surface.freezeDisplay(0);
- }
- } else {
- Surface.freezeDisplay(0);
+ // TODO(multidisplay): rotation on main screen only.
+ final Display display = displayContent.getDisplay();
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ screenRotationAnimation = new ScreenRotationAnimation(mContext,
+ display, mFxSession, inTransaction, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, display.getRotation(),
+ exitAnim, enterAnim);
+ mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);
}
}
@@ -9348,41 +9932,52 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen) {
+ if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen
+ || mClientFreezingScreen) {
if (DEBUG_ORIENTATION) Slog.d(TAG,
"stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+ ", mAppsFreezingScreen=" + mAppsFreezingScreen
- + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen);
+ + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
+ + ", mClientFreezingScreen=" + mClientFreezingScreen);
return;
}
mDisplayFrozen = false;
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+ mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
if (PROFILE_ORIENTATION) {
Debug.stopMethodTracing();
}
boolean updateRotation = false;
-
- if (CUSTOM_SCREEN_ROTATION && mAnimator.mScreenRotationAnimation != null
- && mAnimator.mScreenRotationAnimation.hasScreenshot()) {
+
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ final int displayId = displayContent.getDisplayId();
+ ScreenRotationAnimation screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(displayId);
+ if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
+ && screenRotationAnimation.hasScreenshot()) {
if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation");
- if (mAnimator.mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
- mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) {
- scheduleAnimationLocked();
+ // TODO(multidisplay): rotation on main screen only.
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+ mTransitionAnimationScale, displayInfo.logicalWidth,
+ displayInfo.logicalHeight)) {
+ updateLayoutToAnimationLocked();
} else {
- mAnimator.mScreenRotationAnimation.kill();
- mAnimator.mScreenRotationAnimation = null;
+ screenRotationAnimation.kill();
+ screenRotationAnimation = null;
+ mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);
updateRotation = true;
}
} else {
- if (mAnimator.mScreenRotationAnimation != null) {
- mAnimator.mScreenRotationAnimation.kill();
- mAnimator.mScreenRotationAnimation = null;
+ if (screenRotationAnimation != null) {
+ screenRotationAnimation.kill();
+ screenRotationAnimation = null;
+ mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);
}
updateRotation = true;
}
- Surface.unfreezeDisplay(0);
mInputMonitor.thawInputDispatchingLw();
@@ -9434,7 +10029,7 @@ public class WindowManagerService extends IWindowManager.Stub
return val;
}
- void createWatermark() {
+ void createWatermarkInTransaction() {
if (mWatermark != null) {
return;
}
@@ -9448,7 +10043,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(getDefaultDisplayContentLocked().getDisplay(),
+ mRealDisplayMetrics, mFxSession, toks);
}
}
} catch (FileNotFoundException e) {
@@ -9478,11 +10074,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 = getDefaultWindowListLocked();
+ 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 +10147,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 = getDefaultDisplayContentLocked();
if (mInputMethodWindow != null) {
mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
}
@@ -9699,10 +10299,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);
}
@@ -9809,28 +10411,12 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(": "); pw.println(pair.second);
}
}
- 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);
+ pw.println(" DisplayContents");
+ if (mDisplayReady) {
+ DisplayContentsIterator dCIterator = new DisplayContentsIterator();
+ while (dCIterator.hasNext()) {
+ dCIterator.next().dump(" ", pw);
+ }
} else {
pw.println(" NO DISPLAY");
}
@@ -9847,7 +10433,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));
@@ -9871,21 +10458,28 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
- pw.print(" mLayoutNeeded="); pw.print(mLayoutNeeded);
- pw.print("mTransactionSequence="); pw.println(mTransactionSequence);
+ if (needsLayout()) {
+ pw.print(" layoutNeeded on displays=");
+ DisplayContentsIterator dcIterator = new DisplayContentsIterator();
+ while (dcIterator.hasNext()) {
+ final DisplayContent displayContent = dcIterator.next();
+ if (displayContent.layoutNeeded) {
+ pw.print(displayContent.getDisplayId());
+ }
+ }
+ pw.println();
+ }
+ pw.print(" mTransactionSequence="); pw.println(mTransactionSequence);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
- pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
- pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
- pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig);
+ pw.print(" windows="); pw.print(mWindowsFreezingScreen);
+ pw.print(" client="); pw.print(mClientFreezingScreen);
+ pw.print(" apps="); pw.print(mAppsFreezingScreen);
+ pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
pw.print(" mRotation="); pw.print(mRotation);
pw.print(" mAltOrientation="); pw.println(mAltOrientation);
pw.print(" mLastWindowForcedOrientation="); pw.print(mLastWindowForcedOrientation);
pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
- if (mAnimator.mScreenRotationAnimation != null) {
- pw.println(" mScreenRotationAnimation:");
- mAnimator.mScreenRotationAnimation.printTo(" ", pw);
- }
pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
pw.print(" mTransitionWindowAnimationScale="); pw.print(mTransitionAnimationScale);
pw.print(" mAnimatorDurationScale="); pw.println(mAnimatorDurationScale);
@@ -9916,15 +10510,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) {
@@ -9934,17 +10528,18 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
pw.print(" mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
pw.println(" Window Animator:");
- mAnimator.dump(pw, " ", dumpAll);
+ mAnimator.dumpLocked(pw, " ", dumpAll);
}
}
boolean dumpWindows(PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll) {
- ArrayList<WindowState> windows = new ArrayList<WindowState>();
+ WindowList windows = new WindowList();
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 +10554,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 +10729,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 +10742,155 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void bulkSetParameters(final int bulkUpdateParams, int pendingLayoutChanges) {
- mH.sendMessage(mH.obtainMessage(H.BULK_UPDATE_PARAMETERS, bulkUpdateParams,
- pendingLayoutChanges));
+ public void createDisplayContentLocked(final Display display) {
+ if (display == null) {
+ throw new IllegalArgumentException("getDisplayContent: display must not be null");
+ }
+ final DisplayContent displayContent = new DisplayContent(display);
+ mDisplayContents.put(display.getDisplayId(), displayContent);
+ }
+
+ public DisplayContent getDisplayContentLocked(final int displayId) {
+ DisplayContent displayContent = mDisplayContents.get(displayId);
+ if (displayContent == null) {
+ displayContent = new DisplayContent(mDisplayManager.getDisplay(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");
+ }
+ }
+
+ final static 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 getDefaultDisplayContentLocked() {
+ return getDisplayContentLocked(Display.DEFAULT_DISPLAY);
+ }
+
+ public WindowList getDefaultWindowListLocked() {
+ return getDefaultDisplayContentLocked().getWindowList();
+ }
+
+ public DisplayInfo getDefaultDisplayInfoLocked() {
+ return getDefaultDisplayContentLocked().getDisplayInfo();
+ }
+
+ public WindowList getWindowListLocked(final Display display) {
+ return getDisplayContentLocked(display.getDisplayId()).getWindowList();
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
+ }
+
+ private void handleDisplayAddedLocked(int displayId) {
+ createDisplayContentLocked(mDisplayManager.getDisplay(displayId));
+ displayReady(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_REMOVED, displayId, 0));
+ }
+
+ private void handleDisplayRemovedLocked(int displayId) {
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ mDisplayContents.delete(displayId);
+ WindowList windows = displayContent.getWindowList();
+ for (int i = windows.size() - 1; i >= 0; --i) {
+ final WindowState win = windows.get(i);
+ removeWindowLocked(win.mSession, win);
+ }
+ mAnimator.removeDisplayLocked(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_CHANGED, displayId, 0));
+ }
+
+ private void handleDisplayChangedLocked(int displayId) {
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent != null) {
+ displayContent.updateDisplayInfo();
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index a00e8a5..b62028e 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) {
@@ -496,6 +514,21 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
}
+ MagnificationSpec getWindowMagnificationSpecLocked() {
+ MagnificationSpec spec = mDisplayContent.mMagnificationSpec;
+ if (spec != null && !spec.isNop()) {
+ if (mAttachedWindow != null) {
+ if (!mPolicy.canMagnifyWindow(mAttachedWindow.mAttrs)) {
+ return null;
+ }
+ }
+ if (!mPolicy.canMagnifyWindow(mAttrs)) {
+ return null;
+ }
+ }
+ return spec;
+ }
+
@Override
public Rect getFrameLw() {
return mFrame;
@@ -544,6 +577,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,20 +592,22 @@ 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);
}
}
+ @Override
public int getSystemUiVisibility() {
return mSystemUiVisibility;
}
+ @Override
public int getSurfaceLayer() {
return mLayer;
}
@@ -579,7 +615,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
-
+
+ public int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
public long getInputDispatchingTimeoutNanos() {
return mAppToken != null
? mAppToken.inputDispatchingTimeoutNanos
@@ -766,7 +806,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mViewVisibility == View.GONE
|| !mRelayoutCalled
|| (atoken == null && mRootToken.hidden)
- || (atoken != null && (atoken.hiddenRequested || atoken.hidden))
+ || (atoken != null && atoken.hiddenRequested)
|| mAttachedHidden
|| mExiting || mDestroying;
}
@@ -884,6 +924,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 +952,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_ENTER, true);
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mService.updateLayoutToAnimationLocked();
}
return true;
}
@@ -950,7 +995,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mService.updateLayoutToAnimationLocked();
}
return true;
}
@@ -960,6 +1005,22 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mClient.asBinder().isBinderAlive();
}
+ @Override
+ public boolean isDefaultDisplay() {
+ return mDisplayContent.isDefaultDisplay;
+ }
+
+ boolean isOtherUsersAppWindow() {
+ final int type = mAttrs.type;
+ if ((UserHandle.getUserId(mOwnerUid) != mService.mCurrentUserId)
+ && (mOwnerUid != Process.SYSTEM_UID)
+ && (type >= TYPE_BASE_APPLICATION) && (type <= LAST_APPLICATION_WINDOW)
+ && (type != TYPE_APPLICATION_STARTING)) {
+ return true;
+ }
+ return false;
+ }
+
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
outRegion.set(
frame.left + inset.left, frame.top + inset.top,
@@ -988,8 +1049,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,11 +1126,15 @@ 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);
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
- pw.print(" mShownFrame="); mShownFrame.printShortString(pw); pw.println();
+ pw.print(" mShownFrame="); mShownFrame.printShortString(pw);
+ pw.print(" isReadyForDisplay()="); pw.println(isReadyForDisplay());
if (dumpAll) {
pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
pw.print(" last="); mLastFrame.printShortString(pw);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 03e52fe..000a191 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -3,9 +3,7 @@
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 +15,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 +30,16 @@ import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.ArrayList;
+class WinAnimatorList extends ArrayList<WindowStateAnimator> {
+ public WinAnimatorList() {
+ super();
+ }
+
+ public WinAnimatorList(WinAnimatorList other) {
+ super(other);
+ }
+}
+
/**
* Keep track of animations and surface operations for a single WindowState.
**/
@@ -48,13 +57,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 +154,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 +202,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 +235,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 +262,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 +280,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 +328,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
@@ -318,7 +341,7 @@ class WindowStateAnimator {
+ mWin.mPolicyVisibilityAfterAnim);
}
mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
- mService.mLayoutNeeded = true;
+ mWin.mDisplayContent.layoutNeeded = true;
if (!mWin.mPolicyVisibility) {
if (mService.mCurrentFocus == mWin) {
mService.mFocusMayChange = true;
@@ -342,9 +365,10 @@ class WindowStateAnimator {
}
finishExit();
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ final int displayId = mWin.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
- "WindowStateAnimator", mAnimator.mPendingLayoutChanges);
+ "WindowStateAnimator", mAnimator.mPendingLayoutChanges.get(displayId));
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -460,22 +484,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)
+ String name, int w, int h, int format, int flags)
throws OutOfResourcesException {
- super(s, pid, name, display, w, h, format, flags);
- mName = name;
+ super(s, name, 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 +550,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 +597,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();
@@ -592,7 +615,7 @@ class WindowStateAnimator {
mService.makeWindowFreezingScreenIfNeededLocked(mWin);
- int flags = 0;
+ int flags = Surface.HIDDEN;
final WindowManager.LayoutParams attrs = mWin.mAttrs;
if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
@@ -636,14 +659,14 @@ class WindowStateAnimator {
}
if (DEBUG_SURFACE_TRACE) {
mSurface = new SurfaceTrace(
- mSession.mSurfaceSession, mSession.mPid,
+ mSession.mSurfaceSession,
attrs.getTitle().toString(),
- 0, w, h, format, flags);
+ w, h, format, flags);
} else {
mSurface = new Surface(
- mSession.mSurfaceSession, mSession.mPid,
+ mSession.mSurfaceSession,
attrs.getTitle().toString(),
- 0, w, h, format, flags);
+ w, h, format, flags);
}
mWin.mHasSurface = true;
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
@@ -685,14 +708,10 @@ class WindowStateAnimator {
mSurfaceY = mWin.mFrame.top + mWin.mYOffset;
mSurface.setPosition(mSurfaceX, mSurfaceY);
mSurfaceLayer = mAnimLayer;
+ mSurface.setLayerStack(mLayerStack);
mSurface.setLayer(mAnimLayer);
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 +818,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);
@@ -831,8 +847,11 @@ class WindowStateAnimator {
}
}
- final boolean screenAnimation = mService.mAnimator.mScreenRotationAnimation != null
- && mService.mAnimator.mScreenRotationAnimation.isAnimating();
+ final int displayId = mWin.getDisplayId();
+ final ScreenRotationAnimation screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(displayId);
+ final boolean screenAnimation =
+ screenRotationAnimation != null && screenRotationAnimation.isAnimating();
if (selfTransformation || attachedTransformation != null
|| appTransformation != null || screenAnimation) {
// cache often used attributes locally
@@ -870,9 +889,16 @@ 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());
+ tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
+ }
+ MagnificationSpec spec = mWin.getWindowMagnificationSpecLocked();
+ if (spec != null && !spec.isNop()) {
+ tmpMatrix.postScale(spec.mScale, spec.mScale);
+ tmpMatrix.postTranslate(spec.mOffsetX, spec.mOffsetY);
}
// "convert" it into SurfaceFlinger's format
@@ -913,24 +939,27 @@ 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();
+ mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
}
} else {
//Slog.i(TAG, "Not applying alpha transform");
}
- if (WindowManagerService.localLOGV && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
- TAG, "computeShownFrameLocked: Animating " + this +
- " mAlpha=" + mAlpha +
- " self=" + (selfTransformation ? mTransformation.getAlpha() : "null") +
- " attached=" + (attachedTransformation == null ? "null" : attachedTransformation.getAlpha()) +
- " app=" + (appTransformation == null ? "null" : appTransformation.getAlpha()) +
- " screen=" + (screenAnimation ? mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getAlpha()
- : "null"));
+ 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")
+ + " attached=" + (attachedTransformation == null ?
+ "null" : attachedTransformation.getAlpha())
+ + " app=" + (appTransformation == null ? "null" : appTransformation.getAlpha())
+ + " screen=" + (screenAnimation ?
+ screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
return;
- } else if (mWin.mIsWallpaper &&
+ } else if (mIsWallpaper &&
(mAnimator.mPendingActions & WindowAnimator.WALLPAPER_ACTION_PENDING) != 0) {
return;
}
@@ -938,53 +967,113 @@ 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;
+
+ final boolean applyUniverseTransformation = (mAnimator.mUniverseBackground != null
+ && mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
+ && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
+ MagnificationSpec spec = mWin.getWindowMagnificationSpecLocked();
+ if (applyUniverseTransformation || spec != null) {
+ 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);
+
+ if (applyUniverseTransformation) {
+ tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
+ }
+
+ if (spec != null && !spec.isNop()) {
+ tmpMatrix.postScale(spec.mScale, spec.mScale);
+ tmpMatrix.postTranslate(spec.mOffsetX, spec.mOffsetY);
+ }
+
+ 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;
+ if (applyUniverseTransformation) {
+ mShownAlpha *= 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) {
final WindowState w = mWin;
+ DisplayInfo displayInfo = w.mDisplayContent.getDisplayInfo();
// Need to recompute a new system decor rect each time.
if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
// Currently can't do this cropping for scaled windows. We'll
// just keep the crop rect the same as the source surface.
w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
+ } else if (!w.isDefaultDisplay()) {
+ // On a different display is easy, just use the entire display.
+ w.mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
} 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)) {
@@ -1003,7 +1092,7 @@ class WindowStateAnimator {
}
}
- void setSurfaceBoundaries(final boolean recoveringMemory) {
+ void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
int width, height;
if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -1053,11 +1142,13 @@ class WindowStateAnimator {
"SIZE " + width + "x" + height, null);
mSurfaceResized = true;
mSurface.setSize(width, height);
- mAnimator.mPendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ final int displayId = w.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId,
+ 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.startDimmingLocked(this, w.mExiting ? 0 : w.mAttrs.dimAmount,
+ displayInfo.appWidth, displayInfo.appHeight);
}
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
@@ -1090,9 +1181,9 @@ class WindowStateAnimator {
computeShownFrameLocked();
- setSurfaceBoundaries(recoveringMemory);
+ setSurfaceBoundariesLocked(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 +1242,7 @@ class WindowStateAnimator {
+ " during relayout");
if (showSurfaceRobustlyLocked()) {
mLastHidden = false;
- if (w.mIsWallpaper) {
+ if (mIsWallpaper) {
mService.dispatchWallpaperVisibility(w, true);
}
} else {
@@ -1169,8 +1260,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 +1269,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 +1330,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 +1354,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 +1370,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 +1382,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) {
@@ -1301,7 +1397,7 @@ class WindowStateAnimator {
// do a layout. If called from within the transaction
// loop, this will cause it to restart with a new
// layout.
- mService.mLayoutNeeded = true;
+ c.mDisplayContent.layoutNeeded = true;
}
}
}
@@ -1370,8 +1466,8 @@ class WindowStateAnimator {
} else {
transit = WindowManagerPolicy.TRANSIT_SHOW;
}
-
applyAnimationLocked(transit, true);
+ mService.scheduleNotifyWindowTranstionIfNeededLocked(mWin, transit);
}
// TODO(cmautner): Move back to WindowState?
@@ -1483,6 +1579,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);