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/AppWidgetService.java2
-rw-r--r--services/java/com/android/server/AppWidgetServiceImpl.java114
-rw-r--r--services/java/com/android/server/BackupManagerService.java23
-rw-r--r--services/java/com/android/server/BatteryService.java29
-rwxr-xr-xservices/java/com/android/server/BluetoothManagerService.java740
-rw-r--r--services/java/com/android/server/ClipboardService.java8
-rw-r--r--services/java/com/android/server/CommonTimeManagementService.java2
-rw-r--r--services/java/com/android/server/ConnectivityService.java483
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java6
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java109
-rw-r--r--services/java/com/android/server/DockObserver.java231
-rw-r--r--services/java/com/android/server/EventLogTags.logtags2
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java32
-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.java2358
-rw-r--r--services/java/com/android/server/MountService.java4
-rw-r--r--services/java/com/android/server/NativeDaemonConnector.java129
-rw-r--r--services/java/com/android/server/NetworkManagementService.java210
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java25
-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/SystemServer.java85
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java9
-rw-r--r--services/java/com/android/server/ThrottleService.java1
-rw-r--r--services/java/com/android/server/TwilightService.java572
-rw-r--r--services/java/com/android/server/UiModeManagerService.java343
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java26
-rw-r--r--services/java/com/android/server/Watchdog.java3
-rw-r--r--services/java/com/android/server/WifiService.java87
-rw-r--r--services/java/com/android/server/WiredAccessoryObserver.java305
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityInputFilter.java3
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java153
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java2
-rw-r--r--services/java/com/android/server/am/ActiveServices.java2134
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java2625
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java12
-rwxr-xr-xservices/java/com/android/server/am/ActivityStack.java19
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java20
-rw-r--r--services/java/com/android/server/am/BroadcastFilter.java4
-rw-r--r--services/java/com/android/server/am/BroadcastQueue.java79
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java4
-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.java8
-rw-r--r--services/java/com/android/server/am/IntentBindRecord.java18
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java4
-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.java68
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java8
-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/connectivity/Tethering.java35
-rw-r--r--services/java/com/android/server/connectivity/Vpn.java451
-rw-r--r--services/java/com/android/server/display/DisplayAdapter.java48
-rw-r--r--services/java/com/android/server/display/DisplayDevice.java37
-rw-r--r--services/java/com/android/server/display/DisplayDeviceInfo.java62
-rw-r--r--services/java/com/android/server/display/DisplayManagerService.java176
-rw-r--r--services/java/com/android/server/display/HeadlessDisplayAdapter.java62
-rw-r--r--services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java58
-rw-r--r--services/java/com/android/server/input/InputFilter.java258
-rw-r--r--services/java/com/android/server/input/InputManagerService.java54
-rw-r--r--services/java/com/android/server/input/InputWindowHandle.java6
-rw-r--r--services/java/com/android/server/input/PersistentDataStore.java2
-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.java708
-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.java310
-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.java271
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java28
-rw-r--r--services/java/com/android/server/net/NetworkStatsCollection.java2
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java2
-rw-r--r--services/java/com/android/server/pm/Installer.java4
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java1097
-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.java309
-rw-r--r--services/java/com/android/server/pm/UserManager.java465
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java628
-rw-r--r--services/java/com/android/server/power/DisplayPowerController.java1177
-rw-r--r--services/java/com/android/server/power/DisplayPowerRequest.java103
-rw-r--r--services/java/com/android/server/power/DisplayPowerState.java274
-rw-r--r--services/java/com/android/server/power/ElectronBeam.java652
-rw-r--r--services/java/com/android/server/power/Notifier.java441
-rw-r--r--services/java/com/android/server/power/PhotonicModulator.java87
-rw-r--r--services/java/com/android/server/power/PowerManagerService.java2121
-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)17
-rw-r--r--services/java/com/android/server/power/SuspendBlocker.java43
-rw-r--r--services/java/com/android/server/usb/UsbDebuggingManager.java322
-rw-r--r--services/java/com/android/server/usb/UsbDeviceManager.java26
-rw-r--r--services/java/com/android/server/usb/UsbService.java10
-rw-r--r--services/java/com/android/server/wm/AppWindowAnimator.java25
-rw-r--r--services/java/com/android/server/wm/AppWindowToken.java2
-rw-r--r--services/java/com/android/server/wm/BlackFrame.java20
-rw-r--r--services/java/com/android/server/wm/DimAnimator.java6
-rw-r--r--services/java/com/android/server/wm/DimSurface.java6
-rw-r--r--services/java/com/android/server/wm/DisplayContent.java111
-rw-r--r--services/java/com/android/server/wm/DragState.java24
-rw-r--r--services/java/com/android/server/wm/FakeWindowImpl.java3
-rw-r--r--services/java/com/android/server/wm/InputMonitor.java92
-rw-r--r--services/java/com/android/server/wm/KeyguardDisableHandler.java107
-rw-r--r--services/java/com/android/server/wm/ScreenRotationAnimation.java25
-rw-r--r--services/java/com/android/server/wm/Session.java43
-rw-r--r--services/java/com/android/server/wm/StrictModeFlash.java5
-rw-r--r--services/java/com/android/server/wm/Watermark.java7
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java450
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java2209
-rw-r--r--services/java/com/android/server/wm/WindowState.java67
-rw-r--r--services/java/com/android/server/wm/WindowStateAnimator.java280
123 files changed, 19333 insertions, 12140 deletions
diff --git a/services/java/Android.mk b/services/java/Android.mk
index e70a6c9..95b28d9 100644
--- a/services/java/Android.mk
+++ b/services/java/Android.mk
@@ -13,9 +13,6 @@ LOCAL_MODULE:= services
LOCAL_JAVA_LIBRARIES := android.policy telephony-common
-LOCAL_NO_EMMA_INSTRUMENT := true
-LOCAL_NO_EMMA_COMPILE := true
-
include $(BUILD_JAVA_LIBRARY)
include $(BUILD_DROIDDOC)
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 38f4554..8e341df 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -177,7 +177,7 @@ class AppWidgetService extends IAppWidgetService.Stub
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- onUserRemoved(intent.getIntExtra(Intent.EXTRA_USERID, -1));
+ onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
}, userFilter);
}
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index f9c432b..539e561 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -36,24 +36,26 @@ import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.Point;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
+import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.TypedValue;
import android.util.Xml;
+import android.view.Display;
import android.view.WindowManager;
import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetHost;
-import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -188,11 +190,12 @@ class AppWidgetServiceImpl {
void computeMaximumWidgetBitmapMemory() {
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- int height = wm.getDefaultDisplay().getRawHeight();
- int width = wm.getDefaultDisplay().getRawWidth();
+ Display display = wm.getDefaultDisplay();
+ Point size = new Point();
+ display.getRealSize(size);
// Cap memory usage at 1.5 times the size of the display
// 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
- mMaxWidgetBitmapMemory = 6 * width * height;
+ mMaxWidgetBitmapMemory = 6 * size.x * size.y;
}
public void systemReady(boolean safeMode) {
@@ -210,11 +213,19 @@ class AppWidgetServiceImpl {
synchronized (mAppWidgetIds) {
ensureStateLoadedLocked();
- int N = mInstalledProviders.size();
+ // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
+ // list of installed providers and skip providers that we don't need to update.
+ // Also note that remove the provider does not clear the Provider component data.
+ ArrayList<Provider> installedProviders =
+ new ArrayList<Provider>(mInstalledProviders);
+ HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
+ int N = installedProviders.size();
for (int i = N - 1; i >= 0; i--) {
- Provider p = mInstalledProviders.get(i);
- String pkgName = p.info.provider.getPackageName();
- updateProvidersForPackageLocked(pkgName);
+ Provider p = installedProviders.get(i);
+ ComponentName cn = p.info.provider;
+ if (!removedProviders.contains(cn)) {
+ updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
+ }
}
saveStateLocked();
}
@@ -225,6 +236,7 @@ class AppWidgetServiceImpl {
final String action = intent.getAction();
boolean added = false;
boolean changed = false;
+ boolean providersModified = false;
String pkgList[] = null;
if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -256,12 +268,12 @@ class AppWidgetServiceImpl {
|| (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
for (String pkgName : pkgList) {
// The package was just upgraded
- updateProvidersForPackageLocked(pkgName);
+ providersModified |= updateProvidersForPackageLocked(pkgName, null);
}
} else {
// The package was just added
for (String pkgName : pkgList) {
- addProvidersForPackageLocked(pkgName);
+ providersModified |= addProvidersForPackageLocked(pkgName);
}
}
saveStateLocked();
@@ -274,12 +286,20 @@ class AppWidgetServiceImpl {
synchronized (mAppWidgetIds) {
ensureStateLoadedLocked();
for (String pkgName : pkgList) {
- removeProvidersForPackageLocked(pkgName);
+ providersModified |= removeProvidersForPackageLocked(pkgName);
saveStateLocked();
}
}
}
}
+
+ if (providersModified) {
+ // If the set of providers has been modified, notify each active AppWidgetHost
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ notifyHostsForProvidersChangedLocked();
+ }
+ }
}
private void dumpProvider(Provider p, int index, PrintWriter pw) {
@@ -479,7 +499,7 @@ class AppWidgetServiceImpl {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
intent.setComponent(p.info.provider);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
- mContext.sendBroadcast(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
if (p.instances.size() == 0) {
// cancel the future updates
cancelBroadcasts(p);
@@ -487,7 +507,7 @@ class AppWidgetServiceImpl {
// send the broacast saying that the provider is not in use any more
intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
}
}
@@ -573,7 +593,7 @@ class AppWidgetServiceImpl {
private boolean callerHasBindAppWidgetPermission(String packageName) {
int callingUid = Binder.getCallingUid();
try {
- if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) {
+ if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
return false;
}
} catch (Exception e) {
@@ -645,7 +665,7 @@ class AppWidgetServiceImpl {
mBoundRemoteViewsServices.remove(key);
}
- int userId = UserId.getUserId(id.provider.uid);
+ int userId = UserHandle.getUserId(id.provider.uid);
// Bind to the RemoteViewsService (which will trigger a callback to the
// RemoteViewsAdapter.onServiceConnected())
final long token = Binder.clearCallingIdentity();
@@ -736,7 +756,7 @@ class AppWidgetServiceImpl {
}
};
- int userId = UserId.getUserId(id.provider.uid);
+ int userId = UserHandle.getUserId(id.provider.uid);
// Bind to the service and remove the static intent->factory mapping in the
// RemoteViewsService.
final long token = Binder.clearCallingIdentity();
@@ -860,7 +880,7 @@ class AppWidgetServiceImpl {
intent.setComponent(p.info.provider);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
- mContext.sendBroadcast(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
}
@@ -1006,7 +1026,7 @@ class AppWidgetServiceImpl {
}
};
- int userId = UserId.getUserId(id.provider.uid);
+ int userId = UserHandle.getUserId(id.provider.uid);
// Bind to the service and call onDataSetChanged()
final long token = Binder.clearCallingIdentity();
try {
@@ -1185,7 +1205,7 @@ class AppWidgetServiceImpl {
void sendEnableIntentLocked(Provider p) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
@@ -1193,7 +1213,7 @@ class AppWidgetServiceImpl {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
}
@@ -1355,7 +1375,7 @@ class AppWidgetServiceImpl {
throw new IllegalArgumentException("packageName and uid don't match packageName="
+ packageName);
}
- if (!UserId.isSameApp(callingUid, packageUid)) {
+ if (!UserHandle.isSameApp(callingUid, packageUid)) {
throw new IllegalArgumentException("packageName and uid don't match packageName="
+ packageName);
}
@@ -1642,7 +1662,8 @@ class AppWidgetServiceImpl {
getSettingsFile(mUserId).delete();
}
- void addProvidersForPackageLocked(String pkgName) {
+ boolean addProvidersForPackageLocked(String pkgName) {
+ boolean providersAdded = false;
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.setPackage(pkgName);
List<ResolveInfo> broadcastReceivers;
@@ -1652,7 +1673,7 @@ class AppWidgetServiceImpl {
PackageManager.GET_META_DATA, mUserId);
} catch (RemoteException re) {
// Shouldn't happen, local call
- return;
+ return false;
}
final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
for (int i = 0; i < N; i++) {
@@ -1663,11 +1684,21 @@ class AppWidgetServiceImpl {
}
if (pkgName.equals(ai.packageName)) {
addProviderLocked(ri);
+ providersAdded = true;
}
}
+
+ return providersAdded;
}
- void updateProvidersForPackageLocked(String pkgName) {
+ /**
+ * Updates all providers with the specified package names, and records any providers that were
+ * pruned.
+ *
+ * @return whether any providers were updated
+ */
+ boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
+ boolean providersUpdated = false;
HashSet<String> keep = new HashSet<String>();
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.setPackage(pkgName);
@@ -1678,7 +1709,7 @@ class AppWidgetServiceImpl {
PackageManager.GET_META_DATA, mUserId);
} catch (RemoteException re) {
// Shouldn't happen, local call
- return;
+ return false;
}
// add the missing ones and collect which ones to keep
@@ -1695,6 +1726,7 @@ class AppWidgetServiceImpl {
if (p == null) {
if (addProviderLocked(ri)) {
keep.add(ai.name);
+ providersUpdated = true;
}
} else {
Provider parsed = parseProviderInfoXml(component, ri);
@@ -1729,6 +1761,7 @@ class AppWidgetServiceImpl {
}
// Now that we've told the host, push out an update.
sendUpdateIntentLocked(p, appWidgetIds);
+ providersUpdated = true;
}
}
}
@@ -1741,17 +1774,25 @@ class AppWidgetServiceImpl {
Provider p = mInstalledProviders.get(i);
if (pkgName.equals(p.info.provider.getPackageName())
&& !keep.contains(p.info.provider.getClassName())) {
+ if (removedProviders != null) {
+ removedProviders.add(p.info.provider);
+ }
removeProviderLocked(i, p);
+ providersUpdated = true;
}
}
+
+ return providersUpdated;
}
- void removeProvidersForPackageLocked(String pkgName) {
+ boolean removeProvidersForPackageLocked(String pkgName) {
+ boolean providersRemoved = false;
int N = mInstalledProviders.size();
for (int i = N - 1; i >= 0; i--) {
Provider p = mInstalledProviders.get(i);
if (pkgName.equals(p.info.provider.getPackageName())) {
removeProviderLocked(i, p);
+ providersRemoved = true;
}
}
@@ -1766,5 +1807,24 @@ class AppWidgetServiceImpl {
deleteHostLocked(host);
}
}
+
+ return providersRemoved;
+ }
+
+ void notifyHostsForProvidersChangedLocked() {
+ final int N = mHosts.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Host host = mHosts.get(i);
+ try {
+ if (host.callbacks != null) {
+ host.callbacks.providersChanged();
+ }
+ } catch (RemoteException ex) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this
+ // instance.
+ host.callbacks = null;
+ }
+ }
}
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2167c49..8be0ba8 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -65,6 +65,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.os.storage.IMountService;
import android.provider.Settings;
@@ -4845,6 +4846,18 @@ class BackupManagerService extends IBackupManager.Stub {
// ----- IBackupManager binder interface -----
public void dataChanged(final String packageName) {
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_OWNER) {
+ // App is running under a non-owner user profile. For now, we do not back
+ // up data from secondary user profiles.
+ // TODO: backups for all user profiles.
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
+ + callingUserHandle);
+ }
+ return;
+ }
+
final HashSet<String> targets = dataChangedTargets(packageName);
if (targets == null) {
Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
@@ -4937,6 +4950,11 @@ class BackupManagerService extends IBackupManager.Stub {
boolean doAllApps, boolean includeSystem, String[] pkgList) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_OWNER) {
+ throw new IllegalStateException("Backup supported only for the device owner");
+ }
+
// Validate
if (!doAllApps) {
if (!includeShared) {
@@ -5001,6 +5019,11 @@ class BackupManagerService extends IBackupManager.Stub {
public void fullRestore(ParcelFileDescriptor fd) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_OWNER) {
+ throw new IllegalStateException("Restore supported only for the device owner");
+ }
+
long oldId = Binder.clearCallingIdentity();
try {
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index ab9ae69..4192a93 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -68,7 +68,7 @@ import java.util.Arrays;
* a degree Centigrade</p>
* <p>&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 +93,7 @@ class BatteryService extends Binder {
private boolean mAcOnline;
private boolean mUsbOnline;
+ private boolean mWirelessOnline;
private int mBatteryStatus;
private int mBatteryHealth;
private boolean mBatteryPresent;
@@ -148,12 +149,13 @@ class BatteryService extends Binder {
update();
}
- final boolean isPowered() {
+ public final boolean isPowered() {
// assume we are powered if battery state is unknown so the "stay on while plugged in" option will work.
- return (mAcOnline || mUsbOnline || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN);
+ return (mAcOnline || mUsbOnline || mWirelessOnline
+ || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN);
}
- final boolean isPowered(int plugTypeSet) {
+ public final boolean isPowered(int plugTypeSet) {
// assume we are powered if battery state is unknown so
// the "stay on while plugged in" option will work.
if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
@@ -169,10 +171,13 @@ class BatteryService extends Binder {
if (mUsbOnline) {
plugTypeBit |= BatteryManager.BATTERY_PLUGGED_USB;
}
+ if (mWirelessOnline) {
+ plugTypeBit |= BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ }
return (plugTypeSet & plugTypeBit) != 0;
}
- final int getPlugType() {
+ public final int getPlugType() {
return mPlugType;
}
@@ -195,10 +200,15 @@ class BatteryService extends Binder {
};
// returns battery level as a percentage
- final int getBatteryLevel() {
+ public final int getBatteryLevel() {
return mBatteryLevel;
}
+ // true if battery level is below the first warning threshold
+ public final boolean isBatteryLow() {
+ return mBatteryPresent && mBatteryLevel <= mLowBatteryWarningLevel;
+ }
+
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
shutdownIfNoPower();
@@ -243,6 +253,8 @@ class BatteryService extends Binder {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
+ } else if (mWirelessOnline) {
+ mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
@@ -398,6 +410,7 @@ class BatteryService extends Binder {
" temperature: " + mBatteryTemperature +
" technology: " + mBatteryTechnology +
" AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
+ " Wireless powered:" + mWirelessOnline +
" icon:" + icon + " invalid charger:" + mInvalidCharger);
}
@@ -503,6 +516,7 @@ class BatteryService extends Binder {
pw.println("Current Battery Service state:");
pw.println(" AC powered: " + mAcOnline);
pw.println(" USB powered: " + mUsbOnline);
+ pw.println(" Wireless powered: " + mWirelessOnline);
pw.println(" status: " + mBatteryStatus);
pw.println(" health: " + mBatteryHealth);
pw.println(" present: " + mBatteryPresent);
@@ -523,6 +537,8 @@ class BatteryService extends Binder {
mAcOnline = Integer.parseInt(value) != 0;
} else if ("usb".equals(key)) {
mUsbOnline = Integer.parseInt(value) != 0;
+ } else if ("wireless".equals(key)) {
+ mWirelessOnline = Integer.parseInt(value) != 0;
} else if ("status".equals(key)) {
mBatteryStatus = Integer.parseInt(value);
} else if ("level".equals(key)) {
@@ -603,4 +619,3 @@ class BatteryService extends Binder {
}
}
}
-
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
new file mode 100755
index 0000000..4c98ac3
--- /dev/null
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ */
+
+package com.android.server;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.IBluetoothManagerCallback;
+import android.bluetooth.IBluetoothStateChangeCallback;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.provider.Settings;
+import android.util.Log;
+import java.util.List;
+import java.util.ArrayList;
+class BluetoothManagerService extends IBluetoothManager.Stub {
+ private static final String TAG = "BluetoothManagerService";
+ private static final boolean DBG = true;
+
+ private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
+ private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+ private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED";
+ private static final String EXTRA_ACTION="action";
+ private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address";
+ private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
+ private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
+ private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
+
+ private static final int MESSAGE_ENABLE = 1;
+ private static final int MESSAGE_DISABLE = 2;
+ private static final int MESSAGE_REGISTER_ADAPTER = 20;
+ private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
+ private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
+ private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
+ private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
+ private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
+ private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60;
+ private static final int MESSAGE_TIMEOUT_BIND =100;
+ private static final int MESSAGE_TIMEOUT_UNBIND =101;
+ private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
+ private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
+ private static final int MAX_SAVE_RETRIES=3;
+
+ private final Context mContext;
+
+ // Locks are not provided for mName and mAddress.
+ // They are accessed in handler or broadcast receiver, same thread context.
+ private String mAddress;
+ private String mName;
+ private final ContentResolver mContentResolver;
+ private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
+ private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
+ private IBluetooth mBluetooth;
+ private boolean mBinding;
+ private boolean mUnbinding;
+ private boolean mQuietEnable = false;
+
+ private void registerForAirplaneMode(IntentFilter filter) {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final String airplaneModeRadios = Settings.System.getString(resolver,
+ Settings.System.AIRPLANE_MODE_RADIOS);
+ final String toggleableRadios = Settings.System.getString(resolver,
+ Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+ boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true :
+ airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
+ if (mIsAirplaneSensitive) {
+ filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ }
+ }
+
+ private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
+ @Override
+ public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
+ Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState);
+ mHandler.sendMessage(msg);
+ }
+ };
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
+ String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
+ if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName);
+ if (newName != null) {
+ storeNameAndAddress(newName, null);
+ }
+ } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+ if (isAirplaneModeOn()) {
+ // disable without persisting the setting
+ handleDisable(false);
+ } else {
+ if (isBluetoothPersistedStateOn()) {
+ // enable without persisting the setting
+ handleEnable(false, false);
+ }
+ }
+ }
+ }
+ };
+
+ BluetoothManagerService(Context context) {
+ mContext = context;
+ mBluetooth = null;
+ mBinding = false;
+ mUnbinding = false;
+ mAddress = null;
+ mName = null;
+ mContentResolver = context.getContentResolver();
+ mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
+ mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
+ IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+ registerForAirplaneMode(filter);
+ mContext.registerReceiver(mReceiver, filter);
+ boolean airplaneModeOn = isAirplaneModeOn();
+ boolean bluetoothOn = isBluetoothPersistedStateOn();
+ loadStoredNameAndAddress();
+ if (DBG) Log.d(TAG, "airplaneModeOn: " + airplaneModeOn + " bluetoothOn: " + bluetoothOn);
+ if (bluetoothOn) {
+ //Enable
+ if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
+ enable();
+ } else if (!isNameAndAddressSet()) {
+ //Sync the Bluetooth name and address from the Bluetooth Adapter
+ if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
+ getNameAndAddress();
+ }
+ }
+
+ /**
+ * Returns true if airplane mode is currently on
+ */
+ private final boolean isAirplaneModeOn() {
+ return Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_ON, 0) == 1;
+ }
+
+ /**
+ * Returns true if the Bluetooth saved state is "on"
+ */
+ private final boolean isBluetoothPersistedStateOn() {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.BLUETOOTH_ON, 0) ==1;
+ }
+
+ /**
+ * Save the Bluetooth on/off state
+ *
+ */
+ private void persistBluetoothSetting(boolean setOn) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.BLUETOOTH_ON,
+ setOn ? 1 : 0);
+ }
+
+ /**
+ * Returns true if the Bluetooth Adapter's name and address is
+ * locally cached
+ * @return
+ */
+ private boolean isNameAndAddressSet() {
+ return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0;
+ }
+
+ /**
+ * Retrieve the Bluetooth Adapter's name and address and save it in
+ * in the local cache
+ */
+ private void loadStoredNameAndAddress() {
+ if (DBG) Log.d(TAG, "Loading stored name and address");
+ mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
+ mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
+ if (mName == null || mAddress == null) {
+ if (DBG) Log.d(TAG, "Name or address not cached...");
+ }
+ }
+
+ /**
+ * Save the Bluetooth name and address in the persistent store.
+ * Only non-null values will be saved.
+ * @param name
+ * @param address
+ */
+ private void storeNameAndAddress(String name, String address) {
+ if (name != null) {
+ Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
+ mName = name;
+ if (DBG) Log.d(TAG,"Stored Bluetooth name: " +
+ Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
+ }
+
+ if (address != null) {
+ Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
+ mAddress=address;
+ if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " +
+ Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
+ }
+ }
+
+ public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+ "Need BLUETOOTH permission");
+ Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
+ msg.obj = callback;
+ mHandler.sendMessage(msg);
+ synchronized(mConnection) {
+ return mBluetooth;
+ }
+ }
+
+ public void unregisterAdapter(IBluetoothManagerCallback callback) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+ "Need BLUETOOTH permission");
+ Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
+ msg.obj = callback;
+ mHandler.sendMessage(msg);
+ }
+
+ public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+ "Need BLUETOOTH permission");
+ Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
+ msg.obj = callback;
+ mHandler.sendMessage(msg);
+ }
+
+ public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+ "Need BLUETOOTH permission");
+ Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
+ msg.obj = callback;
+ mHandler.sendMessage(msg);
+ }
+
+ public boolean isEnabled() {
+ synchronized(mConnection) {
+ try {
+ return (mBluetooth != null && mBluetooth.isEnabled());
+ } catch (RemoteException e) {
+ Log.e(TAG, "isEnabled()", e);
+ }
+ }
+ return false;
+ }
+
+ public void getNameAndAddress() {
+ if (DBG) {
+ Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
+ " mBinding = " + mBinding);
+ }
+ synchronized(mConnection) {
+ if (mBinding) return;
+ if (mConnection == null) mBinding = true;
+ }
+ Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+ mHandler.sendMessage(msg);
+ }
+ public boolean enableNoAutoConnect()
+ {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permission");
+ if (DBG) {
+ Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth +
+ " mBinding = " + mBinding);
+ }
+ if (Binder.getCallingUid() != android.os.Process.NFC_UID) {
+ throw new SecurityException("no permission to enable Bluetooth quietly");
+ }
+ synchronized(mConnection) {
+ if (mBinding) {
+ Log.w(TAG,"enableNoAutoConnect(): binding in progress. Returning..");
+ return true;
+ }
+ if (mConnection == null) mBinding = true;
+ }
+
+ Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
+ msg.arg1=0; //No persist
+ msg.arg2=1; //Quiet mode
+ mHandler.sendMessage(msg);
+ return true;
+
+ }
+ public boolean enable() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permission");
+ if (DBG) {
+ Log.d(TAG,"enable(): mBluetooth =" + mBluetooth +
+ " mBinding = " + mBinding);
+ }
+
+ synchronized(mConnection) {
+ if (mBinding) {
+ Log.w(TAG,"enable(): binding in progress. Returning..");
+ return true;
+ }
+ if (mConnection == null) mBinding = true;
+ }
+
+ Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
+ msg.arg1=1; //persist
+ msg.arg2=0; //No Quiet Mode
+ mHandler.sendMessage(msg);
+ return true;
+ }
+
+ public boolean disable(boolean persist) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
+ if (DBG) {
+ Log.d(TAG,"disable(): mBluetooth = " + mBluetooth +
+ " mBinding = " + mBinding);
+ }
+
+ synchronized(mConnection) {
+ if (mBluetooth == null) return false;
+ }
+ Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
+ msg.arg1=(persist?1:0);
+ mHandler.sendMessage(msg);
+ return true;
+ }
+
+ public void unbindAndFinish() {
+ if (DBG) {
+ Log.d(TAG,"unbindAndFinish(): " + mBluetooth +
+ " mBinding = " + mBinding);
+ }
+
+ synchronized (mConnection) {
+ if (mUnbinding) return;
+ mUnbinding = true;
+ if (mConnection != null) {
+ if (!mConnection.isGetNameAddressOnly()) {
+ //Unregister callback object
+ try {
+ mBluetooth.unregisterCallback(mBluetoothCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to register BluetoothCallback",re);
+ }
+ }
+ if (DBG) Log.d(TAG, "Sending unbind request.");
+ mBluetooth = null;
+ //Unbind
+ mContext.unbindService(mConnection);
+ mUnbinding = false;
+ } else {
+ mUnbinding=false;
+ }
+ }
+ }
+
+ private void sendBluetoothStateCallback(boolean isUp) {
+ int n = mStateChangeCallbacks.beginBroadcast();
+ if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e);
+ }
+ }
+ mStateChangeCallbacks.finishBroadcast();
+ }
+
+ /**
+ * Inform BluetoothAdapter instances that Adapter service is down
+ */
+ private void sendBluetoothServiceDownCallback() {
+ if (!mConnection.isGetNameAddressOnly()) {
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+ }
+ public String getAddress() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permission");
+ synchronized(mConnection) {
+ if (mBluetooth != null) {
+ try {
+ return mBluetooth.getAddress();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
+ }
+ }
+ }
+ // mAddress is accessed from outside.
+ // It is alright without a lock. Here, bluetooth is off, no other thread is
+ // changing mAddress
+ return mAddress;
+ }
+
+ public String getName() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permission");
+ synchronized(mConnection) {
+ if (mBluetooth != null) {
+ try {
+ return mBluetooth.getName();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
+ }
+ }
+ }
+ // mName is accessed from outside.
+ // It alright without a lock. Here, bluetooth is off, no other thread is
+ // changing mName
+ return mName;
+ }
+
+ private class BluetoothServiceConnection implements ServiceConnection {
+
+ private boolean mGetNameAddressOnly;
+
+ public void setGetNameAddressOnly(boolean getOnly) {
+ mGetNameAddressOnly = getOnly;
+ }
+
+ public boolean isGetNameAddressOnly() {
+ return mGetNameAddressOnly;
+ }
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService");
+ Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
+ msg.obj = service;
+ mHandler.sendMessage(msg);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ // Called if we unexpected disconnected.
+ if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService");
+ Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) Log.d (TAG, "Message: " + msg.what);
+ switch (msg.what) {
+ case MESSAGE_GET_NAME_AND_ADDRESS: {
+ if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
+ synchronized(mConnection) {
+ //Start bind request
+ if (mBluetooth == null) {
+ if (DBG) Log.d(TAG, "Binding to service to get name and address");
+ mConnection.setGetNameAddressOnly(true);
+ //Start bind timeout and bind
+ Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
+ mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
+ Intent i = new Intent(IBluetooth.class.getName());
+ if (!mContext.bindService(i, mConnection,
+ Context.BIND_AUTO_CREATE)) {
+ mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+ Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
+ }
+ }
+ else {
+ Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
+ mHandler.sendMessage(saveMsg);
+ }
+ }
+ break;
+ }
+ case MESSAGE_SAVE_NAME_AND_ADDRESS: {
+ if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS");
+ synchronized(mConnection) {
+ if (mBluetooth != null) {
+ String name = null;
+ String address = null;
+ try {
+ name = mBluetooth.getName();
+ address = mBluetooth.getAddress();
+ } catch (RemoteException re) {
+ Log.e(TAG,"",re);
+ }
+
+ if (name != null && address != null) {
+ storeNameAndAddress(name,address);
+ sendBluetoothServiceDownCallback();
+ unbindAndFinish();
+ } else {
+ if (msg.arg1 < MAX_SAVE_RETRIES) {
+ Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
+ retryMsg.arg1= 1+msg.arg1;
+ if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
+ mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
+ } else {
+ Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
+ sendBluetoothServiceDownCallback();
+ unbindAndFinish();
+ }
+ }
+ }
+ }
+ break;
+ }
+ case MESSAGE_ENABLE:
+ if (DBG) {
+ Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
+ }
+
+ handleEnable(msg.arg1 == 1, msg.arg2 ==1);
+ break;
+
+ case MESSAGE_DISABLE:
+ handleDisable(msg.arg1 == 1);
+ break;
+
+ case MESSAGE_REGISTER_ADAPTER:
+ {
+ IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
+ boolean added = mCallbacks.register(callback);
+ Log.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added );
+ }
+ break;
+ case MESSAGE_UNREGISTER_ADAPTER:
+ {
+ IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
+ boolean removed = mCallbacks.unregister(callback);
+ Log.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed);
+ break;
+ }
+ case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
+ {
+ IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
+ mStateChangeCallbacks.register(callback);
+ break;
+ }
+ case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
+ {
+ IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
+ mStateChangeCallbacks.unregister(callback);
+ break;
+ }
+ case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
+ {
+ if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED");
+
+ //Remove timeout
+ mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+
+ IBinder service = (IBinder) msg.obj;
+ synchronized(mConnection) {
+ mBinding = false;
+ mBluetooth = IBluetooth.Stub.asInterface(service);
+
+ if (mConnection.isGetNameAddressOnly()) {
+ //Request GET NAME AND ADDRESS
+ Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+ mHandler.sendMessage(getMsg);
+ return;
+ }
+
+ //Register callback object
+ try {
+ mBluetooth.registerCallback(mBluetoothCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to register BluetoothCallback",re);
+ }
+
+ //Inform BluetoothAdapter instances that service is up
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+
+ //Do enable request
+ try {
+ if (mQuietEnable == false) {
+ if(!mBluetooth.enable()) {
+ Log.e(TAG,"IBluetooth.enable() returned false");
+ }
+ }
+ else
+ {
+ if(!mBluetooth.enableNoAutoConnect()) {
+ Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG,"Unable to call enable()",e);
+ }
+ }
+ break;
+ }
+ case MESSAGE_TIMEOUT_BIND: {
+ Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
+ synchronized(mConnection) {
+ mBinding = false;
+ }
+ break;
+ }
+ case MESSAGE_BLUETOOTH_STATE_CHANGE:
+ {
+ int prevState = msg.arg1;
+ int newState = msg.arg2;
+ if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
+ if (prevState != newState) {
+ //Notify all proxy objects first of adapter state change
+ if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
+ boolean isUp = (newState==BluetoothAdapter.STATE_ON);
+ sendBluetoothStateCallback(isUp);
+
+ //If Bluetooth is off, send service down event to proxy objects, and unbind
+ if (!isUp) {
+ sendBluetoothServiceDownCallback();
+ unbindAndFinish();
+ }
+ }
+
+ //Send broadcast message to everyone else
+ Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
+ mContext.sendBroadcast(intent,BLUETOOTH_PERM);
+ }
+ break;
+ }
+ case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
+ {
+ if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
+ sendBluetoothServiceDownCallback();
+ break;
+ }
+ case MESSAGE_TIMEOUT_UNBIND:
+ {
+ Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
+ synchronized(mConnection) {
+ mUnbinding = false;
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ private void handleEnable(boolean persist, boolean quietMode) {
+ if (persist) {
+ persistBluetoothSetting(true);
+ }
+
+ mQuietEnable = quietMode;
+
+ synchronized(mConnection) {
+ if (mBluetooth == null) {
+ //Start bind timeout and bind
+ Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
+ mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
+ mConnection.setGetNameAddressOnly(false);
+ Intent i = new Intent(IBluetooth.class.getName());
+ if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE)) {
+ mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+ Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
+ }
+ } else {
+ //Check if name and address is loaded if not get it first.
+ if (!isNameAndAddressSet()) {
+ try {
+ if (DBG) Log.d(TAG,"Getting and storing Bluetooth name and address prior to enable.");
+ storeNameAndAddress(mBluetooth.getName(),mBluetooth.getAddress());
+ } catch (RemoteException e) {Log.e(TAG, "", e);};
+ }
+
+ //Enable bluetooth
+ try {
+ if (!mQuietEnable) {
+ if(!mBluetooth.enable()) {
+ Log.e(TAG,"IBluetooth.enable() returned false");
+ }
+ }
+ else {
+ if(!mBluetooth.enableNoAutoConnect()) {
+ Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG,"Unable to call enable()",e);
+ }
+ }
+ }
+ }
+
+ private void handleDisable(boolean persist) {
+ synchronized(mConnection) {
+ if (mBluetooth != null ) {
+ if (persist) {
+ persistBluetoothSetting(false);
+ }
+ mConnection.setGetNameAddressOnly(false);
+ if (DBG) Log.d(TAG,"Sending off request.");
+
+ try {
+ if(!mBluetooth.disable()) {
+ Log.e(TAG,"IBluetooth.disable() returned false");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG,"Unable to call disable()",e);
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index 8a6a550..0bf7aad 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -36,7 +36,7 @@ import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -96,7 +96,7 @@ public class ClipboardService extends IClipboard.Stub {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_REMOVED.equals(action)) {
- removeClipboard(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ removeClipboard(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
}, userFilter);
@@ -115,7 +115,7 @@ public class ClipboardService extends IClipboard.Stub {
}
private PerUserClipboard getClipboard() {
- return getClipboard(UserId.getCallingUserId());
+ return getClipboard(UserHandle.getCallingUserId());
}
private PerUserClipboard getClipboard(int userId) {
@@ -258,7 +258,7 @@ public class ClipboardService extends IClipboard.Stub {
PackageInfo pi;
try {
pi = mPm.getPackageInfo(pkg, 0);
- if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) {
+ if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {
throw new SecurityException("Calling uid " + uid
+ " does not own package " + pkg);
}
diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java
index 9a25d2e..c316733 100644
--- a/services/java/com/android/server/CommonTimeManagementService.java
+++ b/services/java/com/android/server/CommonTimeManagementService.java
@@ -120,6 +120,8 @@ class CommonTimeManagementService extends Binder {
reevaluateServiceState();
}
public void limitReached(String limitName, String iface) { }
+
+ public void interfaceClassDataActivityChanged(String label, boolean active) {}
};
private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 86ada40..776a1a4 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,18 +16,29 @@
package com.android.server;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
+import static android.net.ConnectivityManager.TYPE_DUMMY;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -35,6 +46,7 @@ import android.net.ConnectivityManager;
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
+import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
@@ -69,6 +81,8 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
@@ -76,21 +90,23 @@ import android.util.SparseIntArray;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
+import com.android.server.net.BaseNetworkObserver;
+import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+
import dalvik.system.DexClassLoader;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationTargetException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -106,13 +122,15 @@ import java.util.List;
* @hide
*/
public class ConnectivityService extends IConnectivityManager.Stub {
+ private static final String TAG = "ConnectivityService";
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private static final String TAG = "ConnectivityService";
private static final boolean LOGD_RULES = false;
+ // TODO: create better separation between radio types and network types
+
// how long to wait before switching back to a radio's default network
private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
// system property that can override the above value
@@ -126,7 +144,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Tethering mTethering;
private boolean mTetheringConfigValid = false;
+ private KeyStore mKeyStore;
+
private Vpn mVpn;
+ private VpnCallback mVpnCallback = new VpnCallback();
+
+ private boolean mLockdownEnabled;
+ private LockdownVpnTracker mLockdownTracker;
/** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
private Object mRulesLock = new Object();
@@ -186,95 +210,83 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final boolean TO_DEFAULT_TABLE = true;
private static final boolean TO_SECONDARY_TABLE = false;
- // Share the event space with NetworkStateTracker (which can't see this
- // internal class but sends us events). If you change these, change
- // NetworkStateTracker.java too.
- private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
- private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
-
/**
* used internally as a delayed event to make us switch back to the
* default network
*/
- private static final int EVENT_RESTORE_DEFAULT_NETWORK =
- MAX_NETWORK_STATE_TRACKER_EVENT + 1;
+ private static final int EVENT_RESTORE_DEFAULT_NETWORK = 1;
/**
* used internally to change our mobile data enabled flag
*/
- private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED =
- MAX_NETWORK_STATE_TRACKER_EVENT + 2;
+ private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
/**
* used internally to change our network preference setting
* arg1 = networkType to prefer
*/
- private static final int EVENT_SET_NETWORK_PREFERENCE =
- MAX_NETWORK_STATE_TRACKER_EVENT + 3;
+ private static final int EVENT_SET_NETWORK_PREFERENCE = 3;
/**
* used internally to synchronize inet condition reports
* arg1 = networkType
* arg2 = condition (0 bad, 100 good)
*/
- private static final int EVENT_INET_CONDITION_CHANGE =
- MAX_NETWORK_STATE_TRACKER_EVENT + 4;
+ private static final int EVENT_INET_CONDITION_CHANGE = 4;
/**
* used internally to mark the end of inet condition hold periods
* arg1 = networkType
*/
- private static final int EVENT_INET_CONDITION_HOLD_END =
- MAX_NETWORK_STATE_TRACKER_EVENT + 5;
+ private static final int EVENT_INET_CONDITION_HOLD_END = 5;
/**
* used internally to set enable/disable cellular data
* arg1 = ENBALED or DISABLED
*/
- private static final int EVENT_SET_MOBILE_DATA =
- MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+ private static final int EVENT_SET_MOBILE_DATA = 7;
/**
* used internally to clear a wakelock when transitioning
* from one net to another
*/
- private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK =
- MAX_NETWORK_STATE_TRACKER_EVENT + 8;
+ private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
/**
* used internally to reload global proxy settings
*/
- private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY =
- MAX_NETWORK_STATE_TRACKER_EVENT + 9;
+ private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = 9;
/**
* used internally to set external dependency met/unmet
* arg1 = ENABLED (met) or DISABLED (unmet)
* arg2 = NetworkType
*/
- private static final int EVENT_SET_DEPENDENCY_MET =
- MAX_NETWORK_STATE_TRACKER_EVENT + 10;
+ private static final int EVENT_SET_DEPENDENCY_MET = 10;
/**
* used internally to restore DNS properties back to the
* default network
*/
- private static final int EVENT_RESTORE_DNS =
- MAX_NETWORK_STATE_TRACKER_EVENT + 11;
+ private static final int EVENT_RESTORE_DNS = 11;
/**
* used internally to send a sticky broadcast delayed.
*/
- private static final int EVENT_SEND_STICKY_BROADCAST_INTENT =
- MAX_NETWORK_STATE_TRACKER_EVENT + 12;
+ private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 12;
/**
* Used internally to
* {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
*/
- private static final int EVENT_SET_POLICY_DATA_ENABLE = MAX_NETWORK_STATE_TRACKER_EVENT + 13;
+ private static final int EVENT_SET_POLICY_DATA_ENABLE = 13;
+
+ private static final int EVENT_VPN_STATE_CHANGED = 14;
- private Handler mHandler;
+ /** Handler used for internal events. */
+ private InternalHandler mHandler;
+ /** Handler used for incoming {@link NetworkStateTracker} events. */
+ private NetworkStateTrackerHandler mTrackerHandler;
// list of DeathRecipients used to make sure features are turned off when
// a process dies
@@ -328,11 +340,24 @@ public class ConnectivityService extends IConnectivityManager.Stub {
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ // Currently, omitting a NetworkFactory will create one internally
+ // TODO: create here when we have cleaner WiMAX support
+ this(context, netd, statsService, policyManager, null);
+ }
+
+ public ConnectivityService(Context context, INetworkManagementService netManager,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager,
+ NetworkFactory netFactory) {
if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
handlerThread.start();
- mHandler = new MyHandler(handlerThread.getLooper());
+ mHandler = new InternalHandler(handlerThread.getLooper());
+ mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper());
+
+ if (netFactory == null) {
+ netFactory = new DefaultNetworkFactory(context, mTrackerHandler);
+ }
// setup our unique device name
if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
@@ -358,8 +383,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
mContext = checkNotNull(context, "missing Context");
- mNetd = checkNotNull(netd, "missing INetworkManagementService");
+ mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
+ mKeyStore = KeyStore.getInstance();
try {
mPolicyManager.registerListener(mPolicyListener);
@@ -472,69 +498,38 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
- /*
- * Create the network state trackers for Wi-Fi and mobile
- * data. Maybe this could be done with a factory class,
- * but it's not clear that it's worth it, given that
- * the number of different network types is not going
- * to change very often.
- */
- for (int netType : mPriorityList) {
- switch (mNetConfigs[netType].radio) {
- case ConnectivityManager.TYPE_WIFI:
- mNetTrackers[netType] = new WifiStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_MOBILE:
- mNetTrackers[netType] = new MobileDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_DUMMY:
- mNetTrackers[netType] = new DummyDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_BLUETOOTH:
- mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_WIMAX:
- mNetTrackers[netType] = makeWimaxStateTracker();
- if (mNetTrackers[netType]!= null) {
- mNetTrackers[netType].startMonitoring(context, mHandler);
- }
- break;
- case ConnectivityManager.TYPE_ETHERNET:
- mNetTrackers[netType] = EthernetDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- default:
- loge("Trying to create a DataStateTracker for an unknown radio type " +
- mNetConfigs[netType].radio);
+
+ // Create and start trackers for hard-coded networks
+ for (int targetNetworkType : mPriorityList) {
+ final NetworkConfig config = mNetConfigs[targetNetworkType];
+ final NetworkStateTracker tracker;
+ try {
+ tracker = netFactory.createTracker(targetNetworkType, config);
+ mNetTrackers[targetNetworkType] = tracker;
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Problem creating " + getNetworkTypeName(targetNetworkType)
+ + " tracker: " + e);
continue;
}
- mCurrentLinkProperties[netType] = null;
- if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {
- mNetTrackers[netType].reconnect();
+
+ tracker.startMonitoring(context, mTrackerHandler);
+ if (config.isDefault()) {
+ tracker.reconnect();
}
}
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
-
- mTethering = new Tethering(mContext, nmService, statsService, this, mHandler.getLooper());
+ mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceTypes().length != 0);
- mVpn = new Vpn(mContext, new VpnCallback());
+ mVpn = new Vpn(mContext, mVpnCallback, mNetd);
+ mVpn.startMonitoring(mContext, mTrackerHandler);
try {
- nmService.registerObserver(mTethering);
- nmService.registerObserver(mVpn);
+ mNetd.registerObserver(mTethering);
+ mNetd.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -548,8 +543,55 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loadGlobalProxy();
}
-private NetworkStateTracker makeWimaxStateTracker() {
- //Initialize Wimax
+
+ /**
+ * Factory that creates {@link NetworkStateTracker} instances using given
+ * {@link NetworkConfig}.
+ */
+ public interface NetworkFactory {
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config);
+ }
+
+ private static class DefaultNetworkFactory implements NetworkFactory {
+ private final Context mContext;
+ private final Handler mTrackerHandler;
+
+ public DefaultNetworkFactory(Context context, Handler trackerHandler) {
+ mContext = context;
+ mTrackerHandler = trackerHandler;
+ }
+
+ @Override
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
+ switch (config.radio) {
+ case TYPE_WIFI:
+ return new WifiStateTracker(targetNetworkType, config.name);
+ case TYPE_MOBILE:
+ return new MobileDataStateTracker(targetNetworkType, config.name);
+ case TYPE_DUMMY:
+ return new DummyDataStateTracker(targetNetworkType, config.name);
+ case TYPE_BLUETOOTH:
+ return BluetoothTetheringDataTracker.getInstance();
+ case TYPE_WIMAX:
+ return makeWimaxStateTracker(mContext, mTrackerHandler);
+ case TYPE_ETHERNET:
+ return EthernetDataTracker.getInstance();
+ default:
+ throw new IllegalArgumentException(
+ "Trying to create a NetworkStateTracker for an unknown radio type: "
+ + config.radio);
+ }
+ }
+ }
+
+ /**
+ * Loads external WiMAX library and registers as system service, returning a
+ * {@link NetworkStateTracker} for WiMAX. Caller is still responsible for
+ * invoking {@link NetworkStateTracker#startMonitoring(Context, Handler)}.
+ */
+ private static NetworkStateTracker makeWimaxStateTracker(
+ Context context, Handler trackerHandler) {
+ // Initialize Wimax
DexClassLoader wimaxClassLoader;
Class wimaxStateTrackerClass = null;
Class wimaxServiceClass = null;
@@ -562,25 +604,25 @@ private NetworkStateTracker makeWimaxStateTracker() {
NetworkStateTracker wimaxStateTracker = null;
- boolean isWimaxEnabled = mContext.getResources().getBoolean(
+ boolean isWimaxEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_wimaxEnabled);
if (isWimaxEnabled) {
try {
- wimaxJarLocation = mContext.getResources().getString(
+ wimaxJarLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceJarLocation);
- wimaxLibLocation = mContext.getResources().getString(
+ wimaxLibLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxNativeLibLocation);
- wimaxManagerClassName = mContext.getResources().getString(
+ wimaxManagerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxManagerClassname);
- wimaxServiceClassName = mContext.getResources().getString(
+ wimaxServiceClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceClassname);
- wimaxStateTrackerClassName = mContext.getResources().getString(
+ wimaxStateTrackerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxStateTrackerClassname);
log("wimaxJarLocation: " + wimaxJarLocation);
wimaxClassLoader = new DexClassLoader(wimaxJarLocation,
- new ContextWrapper(mContext).getCacheDir().getAbsolutePath(),
+ new ContextWrapper(context).getCacheDir().getAbsolutePath(),
wimaxLibLocation, ClassLoader.getSystemClassLoader());
try {
@@ -601,13 +643,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
Constructor wmxStTrkrConst = wimaxStateTrackerClass.getConstructor
(new Class[] {Context.class, Handler.class});
- wimaxStateTracker = (NetworkStateTracker)wmxStTrkrConst.newInstance(mContext,
- mHandler);
+ wimaxStateTracker = (NetworkStateTracker) wmxStTrkrConst.newInstance(
+ context, trackerHandler);
Constructor wmxSrvConst = wimaxServiceClass.getDeclaredConstructor
(new Class[] {Context.class, wimaxStateTrackerClass});
wmxSrvConst.setAccessible(true);
- IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(mContext, wimaxStateTracker);
+ IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(context, wimaxStateTracker);
wmxSrvConst.setAccessible(false);
ServiceManager.addService(WimaxManagerConstants.WIMAX_SERVICE, svcInvoker);
@@ -623,6 +665,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
return wimaxStateTracker;
}
+
/**
* Sets the preferred network.
* @param preference the new preference
@@ -630,7 +673,8 @@ private NetworkStateTracker makeWimaxStateTracker() {
public void setNetworkPreference(int preference) {
enforceChangePermission();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
}
public int getNetworkPreference() {
@@ -749,6 +793,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
}
+ if (mLockdownTracker != null) {
+ info = mLockdownTracker.augmentNetworkInfo(info);
+ }
return info;
}
@@ -766,6 +813,17 @@ private NetworkStateTracker makeWimaxStateTracker() {
return getNetworkInfo(mActiveDefaultNetwork, uid);
}
+ public NetworkInfo getActiveNetworkInfoUnfiltered() {
+ enforceAccessPermission();
+ if (isNetworkTypeValid(mActiveDefaultNetwork)) {
+ final NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
+ if (tracker != null) {
+ return tracker.getNetworkInfo();
+ }
+ }
+ return null;
+ }
+
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid) {
enforceConnectivityInternalPermission();
@@ -923,6 +981,14 @@ private NetworkStateTracker makeWimaxStateTracker() {
return tracker != null && tracker.setRadio(turnOn);
}
+ private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
+ @Override
+ public void interfaceClassDataActivityChanged(String label, boolean active) {
+ int deviceType = Integer.parseInt(label);
+ sendDataActivityBroadcast(deviceType, active);
+ }
+ };
+
/**
* Used to notice when the calling process dies so we can self-expire
*
@@ -1017,6 +1083,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
// TODO - move this into individual networktrackers
int usedNetworkType = convertFeatureToNetworkType(networkType, feature);
+ if (mLockdownEnabled) {
+ // Since carrier APNs usually aren't available from VPN
+ // endpoint, mark them as unavailable.
+ return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
+ }
+
if (mProtectedNetworks.contains(usedNetworkType)) {
enforceConnectivityInternalPermission();
}
@@ -1591,6 +1663,10 @@ private NetworkStateTracker makeWimaxStateTracker() {
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+
+ // Remove idletimer previously setup in {@code handleConnect}
+ removeDataActivityTracking(prevNetType);
+
/*
* If the disconnected network is not the active one, then don't report
* this as a loss of connectivity. What probably happened is that we're
@@ -1610,6 +1686,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
info.setFailover(false);
@@ -1719,7 +1796,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
}
- private void sendConnectedBroadcast(NetworkInfo info) {
+ public void sendConnectedBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
@@ -1734,8 +1811,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
+ if (mLockdownTracker != null) {
+ info = mLockdownTracker.augmentNetworkInfo(info);
+ }
+
Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
info.setFailover(false);
@@ -1759,6 +1841,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
}
+ private void sendDataActivityBroadcast(int deviceType, boolean active) {
+ Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
+ intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
+ intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
+ mContext.sendOrderedBroadcast(intent, RECEIVE_DATA_ACTIVITY_CHANGE);
+ }
+
/**
* Called when an attempt to fail over to another network has failed.
* @param info the {@link NetworkInfo} for the failed network
@@ -1779,6 +1868,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
@@ -1856,14 +1946,35 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
// load the global proxy at startup
mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
+
+ // Try bringing up tracker, but if KeyStore isn't ready yet, wait
+ // for user to unlock device.
+ if (!updateLockdownVpn()) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+ mContext.registerReceiver(mUserPresentReceiver, filter);
+ }
}
+ private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Try creating lockdown tracker, since user present usually means
+ // unlocked keystore.
+ if (updateLockdownVpn()) {
+ mContext.unregisterReceiver(this);
+ }
+ }
+ };
+
private void handleConnect(NetworkInfo info) {
final int type = info.getType();
+ setupDataActivityTracking(type);
+
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
final NetworkStateTracker thisNet = mNetTrackers[type];
+ final String thisIface = thisNet.getLinkProperties().getInterfaceName();
// if this is a default net and other default is running
// kill the one not preferred
@@ -1922,10 +2033,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
// notify battery stats service about this network
- final String iface = thisNet.getLinkProperties().getInterfaceName();
- if (iface != null) {
+ if (thisIface != null) {
try {
- BatteryStatsService.getService().noteNetworkInterfaceType(iface, type);
+ BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, type);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -1933,6 +2043,58 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
/**
+ * Setup data activity tracking for the given network interface.
+ *
+ * Every {@code setupDataActivityTracking} should be paired with a
+ * {@link removeDataActivityTracking} for cleanup.
+ */
+ private void setupDataActivityTracking(int type) {
+ final NetworkStateTracker thisNet = mNetTrackers[type];
+ final String iface = thisNet.getLinkProperties().getInterfaceName();
+
+ final int timeout;
+
+ if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_MOBILE,
+ 0);
+ // Canonicalize mobile network type
+ type = ConnectivityManager.TYPE_MOBILE;
+ } else if (ConnectivityManager.TYPE_WIFI == type) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_WIFI,
+ 0);
+ } else {
+ // do not track any other networks
+ timeout = 0;
+ }
+
+ if (timeout > 0 && iface != null) {
+ try {
+ mNetd.addIdleTimer(iface, timeout, Integer.toString(type));
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Remove data activity tracking when network disconnects.
+ */
+ private void removeDataActivityTracking(int type) {
+ final NetworkStateTracker net = mNetTrackers[type];
+ final String iface = net.getLinkProperties().getInterfaceName();
+
+ if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) ||
+ ConnectivityManager.TYPE_WIFI == type)) {
+ try {
+ // the call fails silently if no idletimer setup for this interface
+ mNetd.removeIdleTimer(iface);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
* After a change in the connectivity state of a network. We're mainly
* concerned with making sure that the list of DNS servers is set up
* according to which networks are connected, and ensuring that the
@@ -2136,9 +2298,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
public void updateNetworkSettings(NetworkStateTracker nt) {
String key = nt.getTcpBufferSizesPropName();
- String bufferSizes = SystemProperties.get(key);
+ String bufferSizes = key == null ? null : SystemProperties.get(key);
- if (bufferSizes.length() == 0) {
+ if (TextUtils.isEmpty(bufferSizes)) {
if (VDBG) log(key + " not found in system properties. Using defaults");
// Setting to default values so we won't be stuck to previous values
@@ -2431,8 +2593,8 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
// must be stateless - things change under us.
- private class MyHandler extends Handler {
- public MyHandler(Looper looper) {
+ private class NetworkStateTrackerHandler extends Handler {
+ public NetworkStateTrackerHandler(Looper looper) {
super(looper);
}
@@ -2482,6 +2644,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
} else if (state == NetworkInfo.State.CONNECTED) {
handleConnect(info);
}
+ if (mLockdownTracker != null) {
+ mLockdownTracker.onNetworkInfoChanged(info);
+ }
break;
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
info = (NetworkInfo) msg.obj;
@@ -2495,6 +2660,19 @@ private NetworkStateTracker makeWimaxStateTracker() {
type = info.getType();
updateNetworkSettings(mNetTrackers[type]);
break;
+ }
+ }
+ }
+
+ private class InternalHandler extends Handler {
+ public InternalHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ NetworkInfo info;
+ switch (msg.what) {
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
String causedBy = null;
synchronized (ConnectivityService.this) {
@@ -2566,6 +2744,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
final int networkType = msg.arg1;
final boolean enabled = msg.arg2 == ENABLED;
handleSetPolicyDataEnable(networkType, enabled);
+ break;
+ }
+ case EVENT_VPN_STATE_CHANGED: {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.onVpnStateChanged((NetworkInfo) msg.obj);
+ }
+ break;
}
}
}
@@ -2907,11 +3092,11 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
}
- private void log(String s) {
+ private static void log(String s) {
Slog.d(TAG, s);
}
- private void loge(String s) {
+ private static void loge(String s) {
Slog.e(TAG, s);
}
@@ -2964,6 +3149,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public boolean protectVpn(ParcelFileDescriptor socket) {
+ throwIfLockdownEnabled();
try {
int type = mActiveDefaultNetwork;
if (ConnectivityManager.isNetworkTypeValid(type)) {
@@ -2990,6 +3176,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public boolean prepareVpn(String oldPackage, String newPackage) {
+ throwIfLockdownEnabled();
return mVpn.prepare(oldPackage, newPackage);
}
@@ -3002,18 +3189,22 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
+ throwIfLockdownEnabled();
return mVpn.establish(config);
}
/**
- * Start legacy VPN and return an intent to VpnDialogs. This method is
- * used by VpnSettings and not available in ConnectivityManager.
- * Permissions are checked in Vpn class.
- * @hide
+ * Start legacy VPN, controlling native daemons as needed. Creates a
+ * secondary thread to perform connection work, returning quickly.
*/
@Override
- public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
- mVpn.startLegacyVpn(config, racoon, mtpd);
+ public void startLegacyVpn(VpnProfile profile) {
+ throwIfLockdownEnabled();
+ final LinkProperties egress = getActiveLinkProperties();
+ if (egress == null) {
+ throw new IllegalStateException("Missing active network connection");
+ }
+ mVpn.startLegacyVpn(profile, mKeyStore, egress);
}
/**
@@ -3024,6 +3215,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
+ throwIfLockdownEnabled();
return mVpn.getLegacyVpnInfo();
}
@@ -3038,10 +3230,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
* be done whenever a better abstraction is developed.
*/
public class VpnCallback {
-
private VpnCallback() {
}
+ public void onStateChanged(NetworkInfo info) {
+ mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
+ }
+
public void override(List<String> dnsServers, List<String> searchDomains) {
if (dnsServers == null) {
restore();
@@ -3108,4 +3303,58 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
}
}
+
+ @Override
+ public boolean updateLockdownVpn() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ // Tear down existing lockdown if profile was removed
+ mLockdownEnabled = LockdownVpnTracker.isEnabled();
+ if (mLockdownEnabled) {
+ if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
+ Slog.w(TAG, "KeyStore locked; unable to create LockdownTracker");
+ return false;
+ }
+
+ final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
+ final VpnProfile profile = VpnProfile.decode(
+ profileName, mKeyStore.get(Credentials.VPN + profileName));
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile));
+ } else {
+ setLockdownTracker(null);
+ }
+
+ return true;
+ }
+
+ /**
+ * Internally set new {@link LockdownVpnTracker}, shutting down any existing
+ * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
+ */
+ private void setLockdownTracker(LockdownVpnTracker tracker) {
+ // Shutdown any existing tracker
+ final LockdownVpnTracker existing = mLockdownTracker;
+ mLockdownTracker = null;
+ if (existing != null) {
+ existing.shutdown();
+ }
+
+ try {
+ if (tracker != null) {
+ mNetd.setFirewallEnabled(true);
+ mLockdownTracker = tracker;
+ mLockdownTracker.init();
+ } else {
+ mNetd.setFirewallEnabled(false);
+ }
+ } catch (RemoteException e) {
+ // ignored; NMS lives inside system_server
+ }
+ }
+
+ private void throwIfLockdownEnabled() {
+ if (mLockdownEnabled) {
+ throw new IllegalStateException("Unavailable in lockdown mode");
+ }
+ }
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index ea19d6e..f966a33 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1626,7 +1626,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mLastMaximumTimeToLock = timeMs;
try {
- getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
+ getIPowerManager().setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
} catch (RemoteException e) {
Slog.w(TAG, "Failure talking with power manager", e);
}
@@ -1667,8 +1667,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
// Power off the display
- mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(),
- WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN);
+ getIPowerManager().goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN);
// Ensure the device is locked
getWindowManager().lockNow();
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 0ed5189..9231674 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -16,6 +16,9 @@
package com.android.server;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -24,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Environment;
import android.os.FileObserver;
@@ -36,8 +40,10 @@ import android.os.StatFs;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.text.format.Formatter;
import android.util.EventLog;
import android.util.Slog;
+import android.util.TimeUtils;
/**
* This class implements a service to monitor the amount of disk
@@ -71,6 +77,7 @@ public class DeviceStorageMonitorService extends Binder {
private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
private static final int DEFAULT_FULL_THRESHOLD_BYTES = 1024*1024; // 1MB
private long mFreeMem; // on /data
+ private long mFreeMemAfterLastCacheClear; // on /data
private long mLastReportedFreeMem;
private long mLastReportedFreeMemTime;
private boolean mLowMemFlag=false;
@@ -95,7 +102,19 @@ public class DeviceStorageMonitorService extends Binder {
private final CacheFileDeletedObserver mCacheFileDeletedObserver;
private static final int _TRUE = 1;
private static final int _FALSE = 0;
+ // This is the raw threshold that has been set at which we consider
+ // storage to be low.
private long mMemLowThreshold;
+ // This is the threshold at which we start trying to flush caches
+ // to get below the low threshold limit. It is less than the low
+ // threshold; we will allow storage to get a bit beyond the limit
+ // before flushing and checking if we are actually low.
+ private long mMemCacheStartTrimThreshold;
+ // This is the threshold that we try to get to when deleting cache
+ // files. This is greater than the low threshold so that we will flush
+ // more files than absolutely needed, to reduce the frequency that
+ // flushing takes place.
+ private long mMemCacheTrimToThreshold;
private int mMemFullThreshold;
/**
@@ -190,7 +209,7 @@ public class DeviceStorageMonitorService extends Binder {
try {
if (localLOGV) Slog.i(TAG, "Clearing cache");
IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
- freeStorageAndNotify(mMemLowThreshold, mClearCacheObserver);
+ freeStorageAndNotify(mMemCacheTrimToThreshold, mClearCacheObserver);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
mClearingCache = false;
@@ -216,24 +235,42 @@ public class DeviceStorageMonitorService extends Binder {
//post intent to NotificationManager to display icon if necessary
if (mFreeMem < mMemLowThreshold) {
- if (!mLowMemFlag) {
- if (checkCache) {
- // See if clearing cache helps
- // Note that clearing cache is asynchronous and so we do a
- // memory check again once the cache has been cleared.
- mThreadStartTime = System.currentTimeMillis();
- mClearSucceeded = false;
- clearCache();
- } else {
+ if (checkCache) {
+ // We are allowed to clear cache files at this point to
+ // try to get down below the limit, because this is not
+ // the initial call after a cache clear has been attempted.
+ // In this case we will try a cache clear if our free
+ // space has gone below the cache clear limit.
+ if (mFreeMem < mMemCacheStartTrimThreshold) {
+ // We only clear the cache if the free storage has changed
+ // a significant amount since the last time.
+ if ((mFreeMemAfterLastCacheClear-mFreeMem)
+ >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
+ // See if clearing cache helps
+ // Note that clearing cache is asynchronous and so we do a
+ // memory check again once the cache has been cleared.
+ mThreadStartTime = System.currentTimeMillis();
+ mClearSucceeded = false;
+ clearCache();
+ }
+ }
+ } else {
+ // This is a call from after clearing the cache. Note
+ // the amount of free storage at this point.
+ mFreeMemAfterLastCacheClear = mFreeMem;
+ if (!mLowMemFlag) {
+ // We tried to clear the cache, but that didn't get us
+ // below the low storage limit. Tell the user.
Slog.i(TAG, "Running low on memory. Sending notification");
sendNotification();
mLowMemFlag = true;
+ } else {
+ if (localLOGV) Slog.v(TAG, "Running low on memory " +
+ "notification already sent. do nothing");
}
- } else {
- if (localLOGV) Slog.v(TAG, "Running low on memory " +
- "notification already sent. do nothing");
}
} else {
+ mFreeMemAfterLastCacheClear = mFreeMem;
if (mLowMemFlag) {
Slog.i(TAG, "Memory available. Cancelling notification");
cancelNotification();
@@ -276,7 +313,7 @@ public class DeviceStorageMonitorService extends Binder {
Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE,
DEFAULT_THRESHOLD_PERCENTAGE);
if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value);
- value *= mTotalMemory;
+ value = (value*mTotalMemory)/100;
long maxValue = Settings.Secure.getInt(
mContentResolver,
Settings.Secure.SYS_STORAGE_THRESHOLD_MAX_BYTES,
@@ -312,8 +349,8 @@ public class DeviceStorageMonitorService extends Binder {
mSystemFileStats = new StatFs(SYSTEM_PATH);
mCacheFileStats = new StatFs(CACHE_PATH);
//initialize total storage on device
- mTotalMemory = ((long)mDataFileStats.getBlockCount() *
- mDataFileStats.getBlockSize())/100L;
+ mTotalMemory = (long)mDataFileStats.getBlockCount() *
+ mDataFileStats.getBlockSize();
mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
@@ -325,6 +362,10 @@ public class DeviceStorageMonitorService extends Binder {
// cache storage thresholds
mMemLowThreshold = getMemThreshold();
mMemFullThreshold = getMemFullThreshold();
+ mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
+ mMemCacheTrimToThreshold = mMemLowThreshold
+ + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
+ mFreeMemAfterLastCacheClear = mTotalMemory;
checkMemory(true);
mCacheFileDeletedObserver = new CacheFileDeletedObserver();
@@ -435,4 +476,40 @@ public class DeviceStorageMonitorService extends Binder {
EventLogTags.writeCacheFileDeleted(path);
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ pw.println("Current DeviceStorageMonitor state:");
+ pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(mContext, mFreeMem));
+ pw.print(" mTotalMemory=");
+ pw.println(Formatter.formatFileSize(mContext, mTotalMemory));
+ pw.print(" mFreeMemAfterLastCacheClear=");
+ pw.println(Formatter.formatFileSize(mContext, mFreeMemAfterLastCacheClear));
+ pw.print(" mLastReportedFreeMem=");
+ pw.print(Formatter.formatFileSize(mContext, mLastReportedFreeMem));
+ pw.print(" mLastReportedFreeMemTime=");
+ TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
+ pw.println();
+ pw.print(" mLowMemFlag="); pw.print(mLowMemFlag);
+ pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
+ pw.print(" mClearSucceeded="); pw.print(mClearSucceeded);
+ pw.print(" mClearingCache="); pw.println(mClearingCache);
+ pw.print(" mMemLowThreshold=");
+ pw.print(Formatter.formatFileSize(mContext, mMemLowThreshold));
+ pw.print(" mMemFullThreshold=");
+ pw.println(Formatter.formatFileSize(mContext, mMemFullThreshold));
+ pw.print(" mMemCacheStartTrimThreshold=");
+ pw.print(Formatter.formatFileSize(mContext, mMemCacheStartTrimThreshold));
+ pw.print(" mMemCacheTrimToThreshold=");
+ pw.println(Formatter.formatFileSize(mContext, mMemCacheTrimToThreshold));
+ }
}
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 64789d3..f1ff27f 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -16,8 +16,9 @@
package com.android.server;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
+import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;
+import static android.provider.Settings.Secure.SCREENSAVER_ENABLED;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -26,11 +27,15 @@ import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.provider.Settings;
-import android.server.BluetoothService;
+import android.service.dreams.IDreamManager;
import android.util.Log;
import android.util.Slog;
@@ -40,14 +45,19 @@ import java.io.FileReader;
/**
* <p>DockObserver monitors for a docking station.
*/
-class DockObserver extends UEventObserver {
+final class DockObserver extends UEventObserver {
private static final String TAG = DockObserver.class.getSimpleName();
private static final boolean LOG = false;
private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
- private static final int MSG_DOCK_STATE = 0;
+ private static final int DEFAULT_SCREENSAVER_ENABLED = 1;
+ private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1;
+
+ private static final int MSG_DOCK_STATE_CHANGED = 0;
+
+ private final Object mLock = new Object();
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -56,11 +66,8 @@ class DockObserver extends UEventObserver {
private final Context mContext;
- private PowerManagerService mPowerManager;
-
- public DockObserver(Context context, PowerManagerService pm) {
+ public DockObserver(Context context) {
mContext = context;
- mPowerManager = pm;
init(); // set initial status
startObserving(DOCK_UEVENT_MATCH);
@@ -72,7 +79,7 @@ class DockObserver extends UEventObserver {
Slog.v(TAG, "Dock UEVENT: " + event.toString());
}
- synchronized (this) {
+ synchronized (mLock) {
try {
int newState = Integer.parseInt(event.get("SWITCH_STATE"));
if (newState != mDockState) {
@@ -86,10 +93,11 @@ class DockObserver extends UEventObserver {
&& mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK
&& mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) ||
mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(),
- false, true);
+ PowerManager pm =
+ (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ pm.wakeUp(SystemClock.uptimeMillis());
}
- update();
+ updateLocked();
}
}
} catch (NumberFormatException e) {
@@ -98,102 +106,147 @@ class DockObserver extends UEventObserver {
}
}
- private final void init() {
- char[] buffer = new char[1024];
-
- try {
- FileReader file = new FileReader(DOCK_STATE_PATH);
- int len = file.read(buffer, 0, 1024);
- file.close();
- mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have dock station support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
+ private void init() {
+ synchronized (mLock) {
+ try {
+ char[] buffer = new char[1024];
+ FileReader file = new FileReader(DOCK_STATE_PATH);
+ try {
+ int len = file.read(buffer, 0, 1024);
+ mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
+ mPreviousDockState = mDockState;
+ } finally {
+ file.close();
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have dock station support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
}
}
void systemReady() {
- synchronized (this) {
+ synchronized (mLock) {
// don't bother broadcasting undocked here
if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- update();
+ updateLocked();
}
mSystemReady = true;
}
}
- private final void update() {
- mHandler.sendEmptyMessage(MSG_DOCK_STATE);
+ private void updateLocked() {
+ mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
}
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DOCK_STATE:
- synchronized (this) {
- Slog.i(TAG, "Dock state changed: " + mDockState);
+ private void handleDockStateChange() {
+ synchronized (mLock) {
+ Slog.i(TAG, "Dock state changed: " + mDockState);
- final ContentResolver cr = mContext.getContentResolver();
+ final ContentResolver cr = mContext.getContentResolver();
- if (Settings.Secure.getInt(cr,
- Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
- Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
- return;
- }
- // Pack up the values and broadcast them to everyone
- Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
-
- // Check if this is Bluetooth Dock
- String address = BluetoothService.readDockBluetoothAddress();
- if (address != null)
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
- BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
-
- // User feedback to confirm dock connection. Particularly
- // useful for flaky contact pins...
- if (Settings.System.getInt(cr,
- Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1)
- {
- String whichSound = null;
- if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
- (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
- (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
- whichSound = Settings.System.DESK_UNDOCK_SOUND;
- } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
- whichSound = Settings.System.CAR_UNDOCK_SOUND;
- }
- } else {
- if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
- (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
- (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
- whichSound = Settings.System.DESK_DOCK_SOUND;
- } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
- whichSound = Settings.System.CAR_DOCK_SOUND;
- }
- }
+ if (Settings.Secure.getInt(cr,
+ Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+ Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
+ return;
+ }
+
+ // Pack up the values and broadcast them to everyone
+ Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
+
+ // Check if this is Bluetooth Dock
+ // TODO(BT): Get Dock address.
+ // String address = null;
+ // if (address != null) {
+ // intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
+ // BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
+ // }
+
+ // User feedback to confirm dock connection. Particularly
+ // useful for flaky contact pins...
+ if (Settings.System.getInt(cr,
+ Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) {
+ String whichSound = null;
+ if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
+ whichSound = Settings.System.DESK_UNDOCK_SOUND;
+ } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
+ whichSound = Settings.System.CAR_UNDOCK_SOUND;
+ }
+ } else {
+ if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
+ whichSound = Settings.System.DESK_DOCK_SOUND;
+ } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
+ whichSound = Settings.System.CAR_DOCK_SOUND;
+ }
+ }
- if (whichSound != null) {
- final String soundPath = Settings.System.getString(cr, whichSound);
- if (soundPath != null) {
- final Uri soundUri = Uri.parse("file://" + soundPath);
- if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
- if (sfx != null) {
- sfx.setStreamType(AudioManager.STREAM_SYSTEM);
- sfx.play();
- }
- }
- }
+ if (whichSound != null) {
+ final String soundPath = Settings.System.getString(cr, whichSound);
+ if (soundPath != null) {
+ final Uri soundUri = Uri.parse("file://" + soundPath);
+ if (soundUri != null) {
+ final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+ if (sfx != null) {
+ sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+ sfx.play();
}
}
+ }
+ }
+ }
- mContext.sendStickyBroadcast(intent);
+ IDreamManager mgr = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams"));
+ if (mgr != null) {
+ // dreams feature enabled
+ boolean undocked = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ if (undocked) {
+ try {
+ if (mgr.isDreaming()) {
+ mgr.awaken();
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to awaken!", e);
}
+ } else {
+ if (isScreenSaverEnabled(mContext) && isScreenSaverActivatedOnDock(mContext)) {
+ try {
+ mgr.dream();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to dream!", e);
+ }
+ }
+ }
+ } else {
+ // dreams feature not enabled, send legacy intent
+ mContext.sendStickyBroadcast(intent);
+ }
+ }
+ }
+
+ private static boolean isScreenSaverEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED) != 0;
+ }
+
+ private static boolean isScreenSaverActivatedOnDock(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0;
+ }
+
+ private final Handler mHandler = new Handler(true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DOCK_STATE_CHANGED:
+ handleDockStateChange();
break;
}
}
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 41f7335..dd50beb 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -114,6 +114,8 @@ option java_package com.android.server
# Package Manager ready:
3100 boot_progress_pms_ready (time|2|3)
# + check activity_launch_time for Home app
+# Value of "unknown sources" setting at app install time
+3110 unknown_sources_enabled (value|1)
# ---------------------------
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index fdb278d..747cf0b 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -16,8 +16,8 @@
package com.android.server;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.AtomicFile;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
@@ -74,6 +74,7 @@ import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
+import android.util.AtomicFile;
import android.util.EventLog;
import android.util.LruCache;
import android.util.Pair;
@@ -2024,7 +2025,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean handleMessage(Message msg) {
- HandlerCaller.SomeArgs args;
+ SomeArgs args;
switch (msg.what) {
case MSG_SHOW_IM_PICKER:
showInputMethodMenu();
@@ -2035,8 +2036,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return true;
case MSG_SHOW_IM_SUBTYPE_ENABLER:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
showInputMethodAndSubtypeEnabler((String)args.arg1);
+ args.recycle();
return true;
case MSG_SHOW_IM_CONFIG:
@@ -2053,48 +2055,53 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return true;
case MSG_BIND_INPUT:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
} catch (RemoteException e) {
}
+ args.recycle();
return true;
case MSG_SHOW_SOFT_INPUT:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).showSoftInput(msg.arg1,
(ResultReceiver)args.arg2);
} catch (RemoteException e) {
}
+ args.recycle();
return true;
case MSG_HIDE_SOFT_INPUT:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).hideSoftInput(0,
(ResultReceiver)args.arg2);
} catch (RemoteException e) {
}
+ args.recycle();
return true;
case MSG_ATTACH_TOKEN:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
} catch (RemoteException e) {
}
+ args.recycle();
return true;
case MSG_CREATE_SESSION:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).createSession(
(IInputMethodCallback)args.arg2);
} catch (RemoteException e) {
}
+ args.recycle();
return true;
// ---------------------------------------------------------
case MSG_START_INPUT:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
SessionState session = (SessionState)args.arg1;
setEnabledSessionInMainThread(session);
@@ -2102,9 +2109,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
(EditorInfo)args.arg3);
} catch (RemoteException e) {
}
+ args.recycle();
return true;
case MSG_RESTART_INPUT:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
SessionState session = (SessionState)args.arg1;
setEnabledSessionInMainThread(session);
@@ -2112,6 +2120,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
(EditorInfo)args.arg3);
} catch (RemoteException e) {
}
+ args.recycle();
return true;
// ---------------------------------------------------------
@@ -2124,13 +2133,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return true;
case MSG_BIND_METHOD:
- args = (HandlerCaller.SomeArgs)msg.obj;
+ args = (SomeArgs)msg.obj;
try {
((IInputMethodClient)args.arg1).onBindMethod(
(InputBindResult)args.arg2);
} catch (RemoteException e) {
Slog.w(TAG, "Client died receiving input method " + args.arg2);
}
+ args.recycle();
return true;
case MSG_SET_ACTIVE:
try {
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index f7e841e..d4769e8 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -34,6 +34,7 @@ import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.LogPrinter;
import android.util.Printer;
+import android.util.StringBuilderPrinter;
import android.content.Intent;
import android.content.IntentFilter;
@@ -65,11 +66,17 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
register_intent_filter(f, f.actionsIterator(),
mTypedActionToFilter, " TypedAction: ");
}
+
+ mOldResolver.addFilter(f);
+ verifyDataStructures(f);
}
public void removeFilter(F f) {
removeFilterInternal(f);
mFilters.remove(f);
+
+ mOldResolver.removeFilter(f);
+ verifyDataStructures(f);
}
void removeFilterInternal(F f) {
@@ -93,18 +100,18 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
boolean dumpMap(PrintWriter out, String titlePrefix, String title,
- String prefix, Map<String, ArrayList<F>> map, String packageName,
+ String prefix, Map<String, F[]> map, String packageName,
boolean printFilter) {
String eprefix = prefix + " ";
String fprefix = prefix + " ";
boolean printedSomething = false;
Printer printer = null;
- for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
- ArrayList<F> a = e.getValue();
- final int N = a.size();
+ for (Map.Entry<String, F[]> e : map.entrySet()) {
+ F[] a = e.getValue();
+ final int N = a.length;
boolean printedHeader = false;
- for (int i=0; i<N; i++) {
- F filter = a.get(i);
+ F filter;
+ for (int i=0; i<N && (filter=a[i]) != null; i++) {
if (packageName != null && !packageName.equals(packageForFilter(filter))) {
continue;
}
@@ -201,7 +208,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
public List<R> queryIntentFromList(Intent intent, String resolvedType,
- boolean defaultOnly, ArrayList<ArrayList<F>> listCut, int userId) {
+ boolean defaultOnly, ArrayList<F[]> listCut, int userId) {
ArrayList<R> resultList = new ArrayList<R>();
final boolean debug = localLOGV ||
@@ -231,10 +238,10 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
TAG, "Resolving type " + resolvedType + " scheme " + scheme
+ " of intent " + intent);
- ArrayList<F> firstTypeCut = null;
- ArrayList<F> secondTypeCut = null;
- ArrayList<F> thirdTypeCut = null;
- ArrayList<F> schemeCut = null;
+ F[] firstTypeCut = null;
+ F[] secondTypeCut = null;
+ F[] thirdTypeCut = null;
+ F[] schemeCut = null;
// If the intent includes a MIME type, then we want to collect all of
// the filters that match that MIME type.
@@ -307,6 +314,14 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
sortResults(finalList);
+ List<R> oldList = mOldResolver.queryIntent(intent, resolvedType, defaultOnly, userId);
+ if (oldList.size() != finalList.size()) {
+ ValidationFailure here = new ValidationFailure();
+ here.fillInStackTrace();
+ Log.wtf(TAG, "Query result " + intent + " size is " + finalList.size()
+ + "; old implementation is " + oldList.size(), here);
+ }
+
if (debug) {
Slog.v(TAG, "Final result list:");
for (R r : finalList) {
@@ -340,7 +355,9 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
* they are to be delivered to.
*/
protected abstract String packageForFilter(F filter);
-
+
+ protected abstract F[] newArray(int size);
+
@SuppressWarnings("unchecked")
protected R newResult(F filter, int match, int userId) {
return (R)filter;
@@ -355,6 +372,29 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
out.print(prefix); out.println(filter);
}
+ private final void addFilter(HashMap<String, F[]> map, String name, F filter) {
+ F[] array = map.get(name);
+ if (array == null) {
+ array = newArray(2);
+ map.put(name, array);
+ array[0] = filter;
+ } else {
+ final int N = array.length;
+ int i = N;
+ while (i > 0 && array[i-1] == null) {
+ i--;
+ }
+ if (i < N) {
+ array[i] = filter;
+ } else {
+ F[] newa = newArray((N*3)/2);
+ System.arraycopy(array, 0, newa, 0, N);
+ newa[N] = filter;
+ map.put(name, newa);
+ }
+ }
+ }
+
private final int register_mime_types(F filter, String prefix) {
final Iterator<String> i = filter.typesIterator();
if (i == null) {
@@ -374,30 +414,12 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
name = name + "/*";
}
- ArrayList<F> array = mTypeToFilter.get(name);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- mTypeToFilter.put(name, array);
- }
- array.add(filter);
+ addFilter(mTypeToFilter, name, filter);
if (slashpos > 0) {
- array = mBaseTypeToFilter.get(baseName);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- mBaseTypeToFilter.put(baseName, array);
- }
- array.add(filter);
+ addFilter(mBaseTypeToFilter, baseName, filter);
} else {
- array = mWildTypeToFilter.get(baseName);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- mWildTypeToFilter.put(baseName, array);
- }
- array.add(filter);
+ addFilter(mWildTypeToFilter, baseName, filter);
}
}
@@ -423,25 +445,19 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
name = name + "/*";
}
- if (!remove_all_objects(mTypeToFilter.get(name), filter)) {
- mTypeToFilter.remove(name);
- }
+ remove_all_objects(mTypeToFilter, name, filter);
if (slashpos > 0) {
- if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) {
- mBaseTypeToFilter.remove(baseName);
- }
+ remove_all_objects(mBaseTypeToFilter, baseName, filter);
} else {
- if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) {
- mWildTypeToFilter.remove(baseName);
- }
+ remove_all_objects(mWildTypeToFilter, baseName, filter);
}
}
return num;
}
private final int register_intent_filter(F filter, Iterator<String> i,
- HashMap<String, ArrayList<F>> dest, String prefix) {
+ HashMap<String, F[]> dest, String prefix) {
if (i == null) {
return 0;
}
@@ -451,19 +467,13 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
String name = i.next();
num++;
if (localLOGV) Slog.v(TAG, prefix + name);
- ArrayList<F> array = dest.get(name);
- if (array == null) {
- //Slog.v(TAG, "Creating new array for " + name);
- array = new ArrayList<F>();
- dest.put(name, array);
- }
- array.add(filter);
+ addFilter(dest, name, filter);
}
return num;
}
private final int unregister_intent_filter(F filter, Iterator<String> i,
- HashMap<String, ArrayList<F>> dest, String prefix) {
+ HashMap<String, F[]> dest, String prefix) {
if (i == null) {
return 0;
}
@@ -473,26 +483,37 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
String name = i.next();
num++;
if (localLOGV) Slog.v(TAG, prefix + name);
- if (!remove_all_objects(dest.get(name), filter)) {
- dest.remove(name);
- }
+ remove_all_objects(dest, name, filter);
}
return num;
}
- private final boolean remove_all_objects(List<F> list, Object object) {
- if (list != null) {
- int N = list.size();
- for (int idx=0; idx<N; idx++) {
- if (list.get(idx) == object) {
- list.remove(idx);
- idx--;
- N--;
+ private final void remove_all_objects(HashMap<String, F[]> map, String name,
+ Object object) {
+ F[] array = map.get(name);
+ if (array != null) {
+ int LAST = array.length-1;
+ while (LAST >= 0 && array[LAST] == null) {
+ LAST--;
+ }
+ for (int idx=LAST; idx>=0; idx--) {
+ if (array[idx] == object) {
+ final int remain = LAST - idx;
+ if (remain > 0) {
+ System.arraycopy(array, idx+1, array, idx, remain);
+ }
+ array[LAST] = null;
+ LAST--;
}
}
- return N > 0;
+ if (LAST < 0) {
+ map.remove(name);
+ } else if (LAST < (array.length/2)) {
+ F[] newa = newArray(LAST+2);
+ System.arraycopy(array, 0, newa, 0, LAST+1);
+ map.put(name, newa);
+ }
}
- return false;
}
private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
@@ -505,18 +526,18 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
boolean debug, boolean defaultOnly,
- String resolvedType, String scheme, List<F> src, List<R> dest, int userId) {
+ String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
final String action = intent.getAction();
final Uri data = intent.getData();
final String packageName = intent.getPackage();
final boolean excludingStopped = intent.isExcludingStopped();
- final int N = src != null ? src.size() : 0;
+ final int N = src != null ? src.length : 0;
boolean hasNonDefaults = false;
int i;
- for (i=0; i<N; i++) {
- F filter = src.get(i);
+ F filter;
+ for (i=0; i<N && (filter=src[i]) != null; i++) {
int match;
if (debug) Slog.v(TAG, "Matching against filter " + filter);
@@ -585,6 +606,120 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
}
};
+ static class ValidationFailure extends RuntimeException {
+ }
+
+ private void verifyDataStructures(IntentFilter src) {
+ compareMaps(src, "mTypeToFilter", mTypeToFilter, mOldResolver.mTypeToFilter);
+ compareMaps(src, "mBaseTypeToFilter", mBaseTypeToFilter, mOldResolver.mBaseTypeToFilter);
+ compareMaps(src, "mWildTypeToFilter", mWildTypeToFilter, mOldResolver.mWildTypeToFilter);
+ compareMaps(src, "mSchemeToFilter", mSchemeToFilter, mOldResolver.mSchemeToFilter);
+ compareMaps(src, "mActionToFilter", mActionToFilter, mOldResolver.mActionToFilter);
+ compareMaps(src, "mTypedActionToFilter", mTypedActionToFilter, mOldResolver.mTypedActionToFilter);
+ }
+
+ private void compareMaps(IntentFilter src, String name, HashMap<String, F[]> cur,
+ HashMap<String, ArrayList<F>> old) {
+ if (cur.size() != old.size()) {
+ StringBuilder missing = new StringBuilder(128);
+ for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) {
+ final F[] curArray = cur.get(e.getKey());
+ if (curArray == null) {
+ if (missing.length() > 0) {
+ missing.append(' ');
+ }
+ missing.append(e.getKey());
+ }
+ }
+ StringBuilder extra = new StringBuilder(128);
+ for (Map.Entry<String, F[]> e : cur.entrySet()) {
+ if (old.get(e.getKey()) == null) {
+ if (extra.length() > 0) {
+ extra.append(' ');
+ }
+ extra.append(e.getKey());
+ }
+ }
+ StringBuilder srcStr = new StringBuilder(1024);
+ StringBuilderPrinter printer = new StringBuilderPrinter(srcStr);
+ src.dump(printer, "");
+ ValidationFailure here = new ValidationFailure();
+ here.fillInStackTrace();
+ Log.wtf(TAG, "New map " + name + " size is " + cur.size()
+ + "; old implementation is " + old.size()
+ + "; missing: " + missing.toString()
+ + "; extra: " + extra.toString()
+ + "; src: " + srcStr.toString(), here);
+ return;
+ }
+ for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) {
+ final F[] curArray = cur.get(e.getKey());
+ int curLen = curArray != null ? curArray.length : 0;
+ if (curLen == 0) {
+ ValidationFailure here = new ValidationFailure();
+ here.fillInStackTrace();
+ Log.wtf(TAG, "New map " + name + " doesn't contain expected key "
+ + e.getKey() + " (array=" + curArray + ")");
+ return;
+ }
+ while (curLen > 0 && curArray[curLen-1] == null) {
+ curLen--;
+ }
+ final ArrayList<F> oldArray = e.getValue();
+ final int oldLen = oldArray.size();
+ if (curLen != oldLen) {
+ ValidationFailure here = new ValidationFailure();
+ here.fillInStackTrace();
+ Log.wtf(TAG, "New map " + name + " entry " + e.getKey() + " size is "
+ + curLen + "; old implementation is " + oldLen, here);
+ return;
+ }
+ for (int i=0; i<oldLen; i++) {
+ F f = oldArray.get(i);
+ boolean found = false;
+ for (int j=0; j<curLen; j++) {
+ if (curArray[j] == f) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ValidationFailure here = new ValidationFailure();
+ here.fillInStackTrace();
+ Log.wtf(TAG, "New map " + name + " entry + " + e.getKey()
+ + " doesn't contain expected filter " + f, here);
+ }
+ }
+ for (int i=0; i<curLen; i++) {
+ if (curArray[i] == null) {
+ ValidationFailure here = new ValidationFailure();
+ here.fillInStackTrace();
+ Log.wtf(TAG, "New map " + name + " entry + " + e.getKey()
+ + " has unexpected null at " + i + "; array: " + curArray, here);
+ break;
+ }
+ }
+ }
+ }
+
+ private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() {
+ @Override protected String packageForFilter(F filter) {
+ return IntentResolver.this.packageForFilter(filter);
+ }
+ @Override protected boolean allowFilterResult(F filter, List<R> dest) {
+ return IntentResolver.this.allowFilterResult(filter, dest);
+ }
+ @Override protected boolean isFilterStopped(F filter, int userId) {
+ return IntentResolver.this.isFilterStopped(filter, userId);
+ }
+ @Override protected R newResult(F filter, int match, int userId) {
+ return IntentResolver.this.newResult(filter, match, userId);
+ }
+ @Override protected void sortResults(List<R> results) {
+ IntentResolver.this.sortResults(results);
+ }
+ };
+
/**
* All filters that have been registered.
*/
@@ -594,16 +729,14 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
* All of the MIME types that have been registered, such as "image/jpeg",
* "image/*", or "{@literal *}/*".
*/
- private final HashMap<String, ArrayList<F>> mTypeToFilter
- = new HashMap<String, ArrayList<F>>();
+ private final HashMap<String, F[]> mTypeToFilter = new HashMap<String, F[]>();
/**
* The base names of all of all fully qualified MIME types that have been
* registered, such as "image" or "*". Wild card MIME types such as
* "image/*" will not be here.
*/
- private final HashMap<String, ArrayList<F>> mBaseTypeToFilter
- = new HashMap<String, ArrayList<F>>();
+ private final HashMap<String, F[]> mBaseTypeToFilter = new HashMap<String, F[]>();
/**
* The base names of all of the MIME types with a sub-type wildcard that
@@ -612,26 +745,21 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
* included here. This also includes the "*" for the "{@literal *}/*"
* MIME type.
*/
- private final HashMap<String, ArrayList<F>> mWildTypeToFilter
- = new HashMap<String, ArrayList<F>>();
+ private final HashMap<String, F[]> mWildTypeToFilter = new HashMap<String, F[]>();
/**
* All of the URI schemes (such as http) that have been registered.
*/
- private final HashMap<String, ArrayList<F>> mSchemeToFilter
- = new HashMap<String, ArrayList<F>>();
+ private final HashMap<String, F[]> mSchemeToFilter = new HashMap<String, F[]>();
/**
* All of the actions that have been registered, but only those that did
* not specify data.
*/
- private final HashMap<String, ArrayList<F>> mActionToFilter
- = new HashMap<String, ArrayList<F>>();
+ private final HashMap<String, F[]> mActionToFilter = new HashMap<String, F[]>();
/**
* All of the actions that have been registered and specified a MIME type.
*/
- private final HashMap<String, ArrayList<F>> mTypedActionToFilter
- = new HashMap<String, ArrayList<F>>();
+ private final HashMap<String, F[]> mTypedActionToFilter = new HashMap<String, F[]>();
}
-
diff --git a/services/java/com/android/server/IntentResolverOld.java b/services/java/com/android/server/IntentResolverOld.java
new file mode 100644
index 0000000..4dd77ce
--- /dev/null
+++ b/services/java/com/android/server/IntentResolverOld.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import android.net.Uri;
+import android.util.FastImmutableArraySet;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Slog;
+import android.util.LogPrinter;
+import android.util.Printer;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+
+/**
+ * Temporary for verification of new implementation.
+ * {@hide}
+ */
+public abstract class IntentResolverOld<F extends IntentFilter, R extends Object> {
+ final private static String TAG = "IntentResolver";
+ final private static boolean DEBUG = false;
+ final private static boolean localLOGV = DEBUG || false;
+
+ public void addFilter(F f) {
+ if (localLOGV) {
+ Slog.v(TAG, "Adding filter: " + f);
+ f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ Slog.v(TAG, " Building Lookup Maps:");
+ }
+
+ mFilters.add(f);
+ int numS = register_intent_filter(f, f.schemesIterator(),
+ mSchemeToFilter, " Scheme: ");
+ int numT = register_mime_types(f, " Type: ");
+ if (numS == 0 && numT == 0) {
+ register_intent_filter(f, f.actionsIterator(),
+ mActionToFilter, " Action: ");
+ }
+ if (numT != 0) {
+ register_intent_filter(f, f.actionsIterator(),
+ mTypedActionToFilter, " TypedAction: ");
+ }
+ }
+
+ public void removeFilter(F f) {
+ removeFilterInternal(f);
+ mFilters.remove(f);
+ }
+
+ void removeFilterInternal(F f) {
+ if (localLOGV) {
+ Slog.v(TAG, "Removing filter: " + f);
+ f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ Slog.v(TAG, " Cleaning Lookup Maps:");
+ }
+
+ int numS = unregister_intent_filter(f, f.schemesIterator(),
+ mSchemeToFilter, " Scheme: ");
+ int numT = unregister_mime_types(f, " Type: ");
+ if (numS == 0 && numT == 0) {
+ unregister_intent_filter(f, f.actionsIterator(),
+ mActionToFilter, " Action: ");
+ }
+ if (numT != 0) {
+ unregister_intent_filter(f, f.actionsIterator(),
+ mTypedActionToFilter, " TypedAction: ");
+ }
+ }
+
+ boolean dumpMap(PrintWriter out, String titlePrefix, String title,
+ String prefix, Map<String, ArrayList<F>> map, String packageName,
+ boolean printFilter) {
+ String eprefix = prefix + " ";
+ String fprefix = prefix + " ";
+ boolean printedSomething = false;
+ Printer printer = null;
+ for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
+ ArrayList<F> a = e.getValue();
+ final int N = a.size();
+ boolean printedHeader = false;
+ for (int i=0; i<N; i++) {
+ F filter = a.get(i);
+ if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ continue;
+ }
+ if (title != null) {
+ out.print(titlePrefix); out.println(title);
+ title = null;
+ }
+ if (!printedHeader) {
+ out.print(eprefix); out.print(e.getKey()); out.println(":");
+ printedHeader = true;
+ }
+ printedSomething = true;
+ dumpFilter(out, fprefix, filter);
+ if (printFilter) {
+ if (printer == null) {
+ printer = new PrintWriterPrinter(out);
+ }
+ filter.dump(printer, fprefix + " ");
+ }
+ }
+ }
+ return printedSomething;
+ }
+
+ public boolean dump(PrintWriter out, String title, String prefix, String packageName,
+ boolean printFilter) {
+ String innerPrefix = prefix + " ";
+ String sepPrefix = "\n" + prefix;
+ String curPrefix = title + "\n" + prefix;
+ if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
+ mTypeToFilter, packageName, printFilter)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
+ mBaseTypeToFilter, packageName, printFilter)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
+ mWildTypeToFilter, packageName, printFilter)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
+ mSchemeToFilter, packageName, printFilter)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
+ mActionToFilter, packageName, printFilter)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
+ mTypedActionToFilter, packageName, printFilter)) {
+ curPrefix = sepPrefix;
+ }
+ return curPrefix == sepPrefix;
+ }
+
+ private class IteratorWrapper implements Iterator<F> {
+ private final Iterator<F> mI;
+ private F mCur;
+
+ IteratorWrapper(Iterator<F> it) {
+ mI = it;
+ }
+
+ public boolean hasNext() {
+ return mI.hasNext();
+ }
+
+ public F next() {
+ return (mCur = mI.next());
+ }
+
+ public void remove() {
+ if (mCur != null) {
+ removeFilterInternal(mCur);
+ }
+ mI.remove();
+ }
+
+ }
+
+ /**
+ * Returns an iterator allowing filters to be removed.
+ */
+ public Iterator<F> filterIterator() {
+ return new IteratorWrapper(mFilters.iterator());
+ }
+
+ /**
+ * Returns a read-only set of the filters.
+ */
+ public Set<F> filterSet() {
+ return Collections.unmodifiableSet(mFilters);
+ }
+
+ public List<R> queryIntentFromList(Intent intent, String resolvedType,
+ boolean defaultOnly, ArrayList<ArrayList<F>> listCut, int userId) {
+ ArrayList<R> resultList = new ArrayList<R>();
+
+ final boolean debug = localLOGV ||
+ ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+
+ FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
+ final String scheme = intent.getScheme();
+ int N = listCut.size();
+ for (int i = 0; i < N; ++i) {
+ buildResolveList(intent, categories, debug, defaultOnly,
+ resolvedType, scheme, listCut.get(i), resultList, userId);
+ }
+ sortResults(resultList);
+ return resultList;
+ }
+
+ public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
+ int userId) {
+ String scheme = intent.getScheme();
+
+ ArrayList<R> finalList = new ArrayList<R>();
+
+ final boolean debug = localLOGV ||
+ ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+
+ if (debug) Slog.v(
+ TAG, "Resolving type " + resolvedType + " scheme " + scheme
+ + " of intent " + intent);
+
+ ArrayList<F> firstTypeCut = null;
+ ArrayList<F> secondTypeCut = null;
+ ArrayList<F> thirdTypeCut = null;
+ ArrayList<F> schemeCut = null;
+
+ // If the intent includes a MIME type, then we want to collect all of
+ // the filters that match that MIME type.
+ if (resolvedType != null) {
+ int slashpos = resolvedType.indexOf('/');
+ if (slashpos > 0) {
+ final String baseType = resolvedType.substring(0, slashpos);
+ if (!baseType.equals("*")) {
+ if (resolvedType.length() != slashpos+2
+ || resolvedType.charAt(slashpos+1) != '*') {
+ // Not a wild card, so we can just look for all filters that
+ // completely match or wildcards whose base type matches.
+ firstTypeCut = mTypeToFilter.get(resolvedType);
+ if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
+ secondTypeCut = mWildTypeToFilter.get(baseType);
+ if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
+ } else {
+ // We can match anything with our base type.
+ firstTypeCut = mBaseTypeToFilter.get(baseType);
+ if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
+ secondTypeCut = mWildTypeToFilter.get(baseType);
+ if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
+ }
+ // Any */* types always apply, but we only need to do this
+ // if the intent type was not already */*.
+ thirdTypeCut = mWildTypeToFilter.get("*");
+ if (debug) Slog.v(TAG, "Third type cut: " + thirdTypeCut);
+ } else if (intent.getAction() != null) {
+ // The intent specified any type ({@literal *}/*). This
+ // can be a whole heck of a lot of things, so as a first
+ // cut let's use the action instead.
+ firstTypeCut = mTypedActionToFilter.get(intent.getAction());
+ if (debug) Slog.v(TAG, "Typed Action list: " + firstTypeCut);
+ }
+ }
+ }
+
+ // If the intent includes a data URI, then we want to collect all of
+ // the filters that match its scheme (we will further refine matches
+ // on the authority and path by directly matching each resulting filter).
+ if (scheme != null) {
+ schemeCut = mSchemeToFilter.get(scheme);
+ if (debug) Slog.v(TAG, "Scheme list: " + schemeCut);
+ }
+
+ // If the intent does not specify any data -- either a MIME type or
+ // a URI -- then we will only be looking for matches against empty
+ // data.
+ if (resolvedType == null && scheme == null && intent.getAction() != null) {
+ firstTypeCut = mActionToFilter.get(intent.getAction());
+ if (debug) Slog.v(TAG, "Action list: " + firstTypeCut);
+ }
+
+ FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
+ if (firstTypeCut != null) {
+ buildResolveList(intent, categories, debug, defaultOnly,
+ resolvedType, scheme, firstTypeCut, finalList, userId);
+ }
+ if (secondTypeCut != null) {
+ buildResolveList(intent, categories, debug, defaultOnly,
+ resolvedType, scheme, secondTypeCut, finalList, userId);
+ }
+ if (thirdTypeCut != null) {
+ buildResolveList(intent, categories, debug, defaultOnly,
+ resolvedType, scheme, thirdTypeCut, finalList, userId);
+ }
+ if (schemeCut != null) {
+ buildResolveList(intent, categories, debug, defaultOnly,
+ resolvedType, scheme, schemeCut, finalList, userId);
+ }
+ sortResults(finalList);
+
+ if (debug) {
+ Slog.v(TAG, "Final result list:");
+ for (R r : finalList) {
+ Slog.v(TAG, " " + r);
+ }
+ }
+ return finalList;
+ }
+
+ /**
+ * Control whether the given filter is allowed to go into the result
+ * list. Mainly intended to prevent adding multiple filters for the
+ * same target object.
+ */
+ protected boolean allowFilterResult(F filter, List<R> dest) {
+ return true;
+ }
+
+ /**
+ * Returns whether the object associated with the given filter is
+ * "stopped," that is whether it should not be included in the result
+ * if the intent requests to excluded stopped objects.
+ */
+ protected boolean isFilterStopped(F filter, int userId) {
+ return false;
+ }
+
+ /**
+ * Return the package that owns this filter. This must be implemented to
+ * provide correct filtering of Intents that have specified a package name
+ * they are to be delivered to.
+ */
+ protected abstract String packageForFilter(F filter);
+
+ @SuppressWarnings("unchecked")
+ protected R newResult(F filter, int match, int userId) {
+ return (R)filter;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void sortResults(List<R> results) {
+ Collections.sort(results, mResolvePrioritySorter);
+ }
+
+ protected void dumpFilter(PrintWriter out, String prefix, F filter) {
+ out.print(prefix); out.println(filter);
+ }
+
+ private final int register_mime_types(F filter, String prefix) {
+ final Iterator<String> i = filter.typesIterator();
+ if (i == null) {
+ return 0;
+ }
+
+ int num = 0;
+ while (i.hasNext()) {
+ String name = i.next();
+ num++;
+ if (localLOGV) Slog.v(TAG, prefix + name);
+ String baseName = name;
+ final int slashpos = name.indexOf('/');
+ if (slashpos > 0) {
+ baseName = name.substring(0, slashpos).intern();
+ } else {
+ name = name + "/*";
+ }
+
+ ArrayList<F> array = mTypeToFilter.get(name);
+ if (array == null) {
+ //Slog.v(TAG, "Creating new array for " + name);
+ array = new ArrayList<F>();
+ mTypeToFilter.put(name, array);
+ }
+ array.add(filter);
+
+ if (slashpos > 0) {
+ array = mBaseTypeToFilter.get(baseName);
+ if (array == null) {
+ //Slog.v(TAG, "Creating new array for " + name);
+ array = new ArrayList<F>();
+ mBaseTypeToFilter.put(baseName, array);
+ }
+ array.add(filter);
+ } else {
+ array = mWildTypeToFilter.get(baseName);
+ if (array == null) {
+ //Slog.v(TAG, "Creating new array for " + name);
+ array = new ArrayList<F>();
+ mWildTypeToFilter.put(baseName, array);
+ }
+ array.add(filter);
+ }
+ }
+
+ return num;
+ }
+
+ private final int unregister_mime_types(F filter, String prefix) {
+ final Iterator<String> i = filter.typesIterator();
+ if (i == null) {
+ return 0;
+ }
+
+ int num = 0;
+ while (i.hasNext()) {
+ String name = i.next();
+ num++;
+ if (localLOGV) Slog.v(TAG, prefix + name);
+ String baseName = name;
+ final int slashpos = name.indexOf('/');
+ if (slashpos > 0) {
+ baseName = name.substring(0, slashpos).intern();
+ } else {
+ name = name + "/*";
+ }
+
+ if (!remove_all_objects(mTypeToFilter.get(name), filter)) {
+ mTypeToFilter.remove(name);
+ }
+
+ if (slashpos > 0) {
+ if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) {
+ mBaseTypeToFilter.remove(baseName);
+ }
+ } else {
+ if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) {
+ mWildTypeToFilter.remove(baseName);
+ }
+ }
+ }
+ return num;
+ }
+
+ private final int register_intent_filter(F filter, Iterator<String> i,
+ HashMap<String, ArrayList<F>> dest, String prefix) {
+ if (i == null) {
+ return 0;
+ }
+
+ int num = 0;
+ while (i.hasNext()) {
+ String name = i.next();
+ num++;
+ if (localLOGV) Slog.v(TAG, prefix + name);
+ ArrayList<F> array = dest.get(name);
+ if (array == null) {
+ //Slog.v(TAG, "Creating new array for " + name);
+ array = new ArrayList<F>();
+ dest.put(name, array);
+ }
+ array.add(filter);
+ }
+ return num;
+ }
+
+ private final int unregister_intent_filter(F filter, Iterator<String> i,
+ HashMap<String, ArrayList<F>> dest, String prefix) {
+ if (i == null) {
+ return 0;
+ }
+
+ int num = 0;
+ while (i.hasNext()) {
+ String name = i.next();
+ num++;
+ if (localLOGV) Slog.v(TAG, prefix + name);
+ if (!remove_all_objects(dest.get(name), filter)) {
+ dest.remove(name);
+ }
+ }
+ return num;
+ }
+
+ private final boolean remove_all_objects(List<F> list, Object object) {
+ if (list != null) {
+ int N = list.size();
+ for (int idx=0; idx<N; idx++) {
+ if (list.get(idx) == object) {
+ list.remove(idx);
+ idx--;
+ N--;
+ }
+ }
+ return N > 0;
+ }
+ return false;
+ }
+
+ private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
+ final Set<String> categories = intent.getCategories();
+ if (categories == null) {
+ return null;
+ }
+ return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
+ }
+
+ private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
+ boolean debug, boolean defaultOnly,
+ String resolvedType, String scheme, List<F> src, List<R> dest, int userId) {
+ final String action = intent.getAction();
+ final Uri data = intent.getData();
+ final String packageName = intent.getPackage();
+
+ final boolean excludingStopped = intent.isExcludingStopped();
+
+ final int N = src != null ? src.size() : 0;
+ boolean hasNonDefaults = false;
+ int i;
+ for (i=0; i<N; i++) {
+ F filter = src.get(i);
+ int match;
+ if (debug) Slog.v(TAG, "Matching against filter " + filter);
+
+ if (excludingStopped && isFilterStopped(filter, userId)) {
+ if (debug) {
+ Slog.v(TAG, " Filter's target is stopped; skipping");
+ }
+ continue;
+ }
+
+ // Is delivery being limited to filters owned by a particular package?
+ if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (debug) {
+ Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
+ }
+ continue;
+ }
+
+ // Do we already have this one?
+ if (!allowFilterResult(filter, dest)) {
+ if (debug) {
+ Slog.v(TAG, " Filter's target already added");
+ }
+ continue;
+ }
+
+ match = filter.match(action, resolvedType, scheme, data, categories, TAG);
+ if (match >= 0) {
+ if (debug) Slog.v(TAG, " Filter matched! match=0x" +
+ Integer.toHexString(match));
+ if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
+ final R oneResult = newResult(filter, match, userId);
+ if (oneResult != null) {
+ dest.add(oneResult);
+ }
+ } else {
+ hasNonDefaults = true;
+ }
+ } else {
+ if (debug) {
+ String reason;
+ switch (match) {
+ case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
+ case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
+ case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
+ case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
+ default: reason = "unknown reason"; break;
+ }
+ Slog.v(TAG, " Filter did not match: " + reason);
+ }
+ }
+ }
+
+ if (dest.size() == 0 && hasNonDefaults) {
+ Slog.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT");
+ }
+ }
+
+ // Sorts a List of IntentFilter objects into descending priority order.
+ @SuppressWarnings("rawtypes")
+ private static final Comparator mResolvePrioritySorter = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ final int q1 = ((IntentFilter) o1).getPriority();
+ final int q2 = ((IntentFilter) o2).getPriority();
+ return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);
+ }
+ };
+
+ /**
+ * All filters that have been registered.
+ */
+ final HashSet<F> mFilters = new HashSet<F>();
+
+ /**
+ * All of the MIME types that have been registered, such as "image/jpeg",
+ * "image/*", or "{@literal *}/*".
+ */
+ final HashMap<String, ArrayList<F>> mTypeToFilter
+ = new HashMap<String, ArrayList<F>>();
+
+ /**
+ * The base names of all of all fully qualified MIME types that have been
+ * registered, such as "image" or "*". Wild card MIME types such as
+ * "image/*" will not be here.
+ */
+ final HashMap<String, ArrayList<F>> mBaseTypeToFilter
+ = new HashMap<String, ArrayList<F>>();
+
+ /**
+ * The base names of all of the MIME types with a sub-type wildcard that
+ * have been registered. For example, a filter with "image/*" will be
+ * included here as "image" but one with "image/jpeg" will not be
+ * included here. This also includes the "*" for the "{@literal *}/*"
+ * MIME type.
+ */
+ final HashMap<String, ArrayList<F>> mWildTypeToFilter
+ = new HashMap<String, ArrayList<F>>();
+
+ /**
+ * All of the URI schemes (such as http) that have been registered.
+ */
+ final HashMap<String, ArrayList<F>> mSchemeToFilter
+ = new HashMap<String, ArrayList<F>>();
+
+ /**
+ * All of the actions that have been registered, but only those that did
+ * not specify data.
+ */
+ final HashMap<String, ArrayList<F>> mActionToFilter
+ = new HashMap<String, ArrayList<F>>();
+
+ /**
+ * All of the actions that have been registered and specified a MIME type.
+ */
+ final HashMap<String, ArrayList<F>> mTypedActionToFilter
+ = new HashMap<String, ArrayList<F>>();
+}
+
diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java
index 1e95f3e..89bfcac 100644
--- a/services/java/com/android/server/LightsService.java
+++ b/services/java/com/android/server/LightsService.java
@@ -31,29 +31,29 @@ public class LightsService {
private static final String TAG = "LightsService";
private static final boolean DEBUG = false;
- static final int LIGHT_ID_BACKLIGHT = 0;
- static final int LIGHT_ID_KEYBOARD = 1;
- static final int LIGHT_ID_BUTTONS = 2;
- static final int LIGHT_ID_BATTERY = 3;
- static final int LIGHT_ID_NOTIFICATIONS = 4;
- static final int LIGHT_ID_ATTENTION = 5;
- static final int LIGHT_ID_BLUETOOTH = 6;
- static final int LIGHT_ID_WIFI = 7;
- static final int LIGHT_ID_COUNT = 8;
-
- static final int LIGHT_FLASH_NONE = 0;
- static final int LIGHT_FLASH_TIMED = 1;
- static final int LIGHT_FLASH_HARDWARE = 2;
+ public static final int LIGHT_ID_BACKLIGHT = 0;
+ public static final int LIGHT_ID_KEYBOARD = 1;
+ public static final int LIGHT_ID_BUTTONS = 2;
+ public static final int LIGHT_ID_BATTERY = 3;
+ public static final int LIGHT_ID_NOTIFICATIONS = 4;
+ public static final int LIGHT_ID_ATTENTION = 5;
+ public static final int LIGHT_ID_BLUETOOTH = 6;
+ public static final int LIGHT_ID_WIFI = 7;
+ public static final int LIGHT_ID_COUNT = 8;
+
+ public static final int LIGHT_FLASH_NONE = 0;
+ public static final int LIGHT_FLASH_TIMED = 1;
+ public static final int LIGHT_FLASH_HARDWARE = 2;
/**
* Light brightness is managed by a user setting.
*/
- static final int BRIGHTNESS_MODE_USER = 0;
+ public static final int BRIGHTNESS_MODE_USER = 0;
/**
* Light brightness is managed by a light sensor.
*/
- static final int BRIGHTNESS_MODE_SENSOR = 1;
+ public static final int BRIGHTNESS_MODE_SENSOR = 1;
private final Light mLights[] = new Light[LIGHT_ID_COUNT];
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 8c1581c..3a45720 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,27 +16,21 @@
package com.android.server;
-import android.app.Activity;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
+import android.location.Geofence;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
@@ -45,9 +39,8 @@ import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
+import android.location.LocationRequest;
import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -57,17 +50,21 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.WorkSource;
import android.provider.Settings;
+import android.provider.Settings.NameValueTable;
import android.util.Log;
import android.util.Slog;
-import android.util.PrintWriterPrinter;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.location.GpsNetInitiatedHandler;
-
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationBlacklist;
+import com.android.server.location.LocationFudger;
import com.android.server.location.LocationProviderInterface;
import com.android.server.location.LocationProviderProxy;
import com.android.server.location.MockProvider;
@@ -76,12 +73,9 @@ import com.android.server.location.PassiveProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
@@ -91,154 +85,260 @@ import java.util.Set;
/**
* The service class that manages LocationProviders and issues location
* updates and alerts.
- *
- * {@hide}
*/
-public class LocationManagerService extends ILocationManager.Stub implements Runnable {
+public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
private static final String TAG = "LocationManagerService";
- private static final boolean LOCAL_LOGV = false;
+ public static final boolean D = false;
- // The last time a location was written, by provider name.
- private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
+ private static final String WAKELOCK_KEY = TAG;
+ private static final String THREAD_NAME = TAG;
private static final String ACCESS_FINE_LOCATION =
- android.Manifest.permission.ACCESS_FINE_LOCATION;
+ android.Manifest.permission.ACCESS_FINE_LOCATION;
private static final String ACCESS_COARSE_LOCATION =
- android.Manifest.permission.ACCESS_COARSE_LOCATION;
+ android.Manifest.permission.ACCESS_COARSE_LOCATION;
private static final String ACCESS_MOCK_LOCATION =
- android.Manifest.permission.ACCESS_MOCK_LOCATION;
+ android.Manifest.permission.ACCESS_MOCK_LOCATION;
private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
- android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
+ android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
private static final String INSTALL_LOCATION_PROVIDER =
- android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+ android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+
+ private static final String NETWORK_LOCATION_SERVICE_ACTION =
+ "com.android.location.service.v2.NetworkLocationProvider";
+ private static final String FUSED_LOCATION_SERVICE_ACTION =
+ "com.android.location.service.FusedLocationProvider";
- private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist";
- private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist";
+ private static final int MSG_LOCATION_CHANGED = 1;
// Location Providers may sometimes deliver location updates
// slightly faster that requested - provide grace period so
// we don't unnecessarily filter events that are otherwise on
// time
- private static final int MAX_PROVIDER_SCHEDULING_JITTER = 100;
+ private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- // Set of providers that are explicitly enabled
- private final Set<String> mEnabledProviders = new HashSet<String>();
+ private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
- // Set of providers that are explicitly disabled
- private final Set<String> mDisabledProviders = new HashSet<String>();
-
- // Locations, status values, and extras for mock providers
- private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
+ private final Context mContext;
- private static boolean sProvidersLoaded = false;
+ // used internally for synchronization
+ private final Object mLock = new Object();
- private final Context mContext;
- private PackageManager mPackageManager; // final after initialize()
- private String mNetworkLocationProviderPackageName; // only used on handler thread
- private String mGeocodeProviderPackageName; // only used on handler thread
+ // --- fields below are final after init() ---
+ private LocationFudger mLocationFudger;
+ private GeofenceManager mGeofenceManager;
+ private PowerManager.WakeLock mWakeLock;
+ private PackageManager mPackageManager;
private GeocoderProxy mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
+ private PassiveProvider mPassiveProvider; // track passive provider for special cases
+ private LocationBlacklist mBlacklist;
- // Cache the real providers for use in addTestProvider() and removeTestProvider()
- LocationProviderProxy mNetworkLocationProvider;
- LocationProviderInterface mGpsLocationProvider;
+ // --- fields below are protected by mWakeLock ---
+ private int mPendingBroadcasts;
- // Handler messages
- private static final int MESSAGE_LOCATION_CHANGED = 1;
- private static final int MESSAGE_PACKAGE_UPDATED = 2;
+ // --- fields below are protected by mLock ---
+ // Set of providers that are explicitly enabled
+ private final Set<String> mEnabledProviders = new HashSet<String>();
- // wakelock variables
- private final static String WAKELOCK_KEY = "LocationManagerService";
- private PowerManager.WakeLock mWakeLock = null;
- private int mPendingBroadcasts;
-
- /**
- * List of all receivers.
- */
- private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
+ // Set of providers that are explicitly disabled
+ private final Set<String> mDisabledProviders = new HashSet<String>();
+ // Mock (test) providers
+ private final HashMap<String, MockProvider> mMockProviders =
+ new HashMap<String, MockProvider>();
- /**
- * List of location providers.
- */
+ // all receivers
+ private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
+
+ // currently installed providers (with mocks replacing real providers)
private final ArrayList<LocationProviderInterface> mProviders =
- new ArrayList<LocationProviderInterface>();
- private final HashMap<String, LocationProviderInterface> mProvidersByName
- = new HashMap<String, LocationProviderInterface>();
+ new ArrayList<LocationProviderInterface>();
- /**
- * Object used internally for synchronization
- */
- private final Object mLock = new Object();
+ // real providers, saved here when mocked out
+ private final HashMap<String, LocationProviderInterface> mRealProviders =
+ new HashMap<String, LocationProviderInterface>();
- /**
- * Mapping from provider name to all its UpdateRecords
- */
- private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
- new HashMap<String,ArrayList<UpdateRecord>>();
+ // mapping from provider name to provider
+ private final HashMap<String, LocationProviderInterface> mProvidersByName =
+ new HashMap<String, LocationProviderInterface>();
- /**
- * Temporary filled in when computing min time for a provider. Access is
- * protected by global lock mLock.
- */
- private final WorkSource mTmpWorkSource = new WorkSource();
+ // mapping from provider name to all its UpdateRecords
+ private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
+ new HashMap<String, ArrayList<UpdateRecord>>();
+
+ // mapping from provider name to last known location
+ private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
+
+ // all providers that operate over proxy, for authorizing incoming location
+ private final ArrayList<LocationProviderProxy> mProxyProviders =
+ new ArrayList<LocationProviderProxy>();
+
+ public LocationManagerService(Context context) {
+ super();
+ mContext = context;
+
+ if (D) Log.d(TAG, "Constructed");
+
+ // most startup is deferred until systemReady()
+ }
+
+ public void systemReady() {
+ Thread thread = new Thread(null, this, THREAD_NAME);
+ thread.start();
+ }
- // Proximity listeners
- private Receiver mProximityReceiver = null;
- private ILocationListener mProximityListener = null;
- private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
- new HashMap<PendingIntent,ProximityAlert>();
- private HashSet<ProximityAlert> mProximitiesEntered =
- new HashSet<ProximityAlert>();
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ Looper.prepare();
+ mLocationHandler = new LocationWorkerHandler();
+ init();
+ Looper.loop();
+ }
+
+ private void init() {
+ if (D) Log.d(TAG, "init()");
+
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+ mPackageManager = mContext.getPackageManager();
- // Last known location for each provider
- private HashMap<String,Location> mLastKnownLocation =
- new HashMap<String,Location>();
+ synchronized (mLock) {
+ loadProvidersLocked();
+ }
+ mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+ mBlacklist.init();
+ mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+ mLocationFudger = new LocationFudger();
+
+ // listen for settings changes
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
+ "(" + NameValueTable.NAME + "=?)",
+ new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
+ ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
+ mLocationHandler);
+ settingsCursor.close();
+ query.addObserver(this);
+ mPackageMonitor.register(mContext, Looper.myLooper(), true);
+ }
+
+ private void loadProvidersLocked() {
+ if (GpsLocationProvider.isSupported()) {
+ // Create a gps location provider
+ GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
+ mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
+ mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
+ addProviderLocked(gpsProvider);
+ mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
+ }
- private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
+ // create a passive location provider, which is always enabled
+ PassiveProvider passiveProvider = new PassiveProvider(this);
+ addProviderLocked(passiveProvider);
+ mEnabledProviders.add(passiveProvider.getName());
+ mPassiveProvider = passiveProvider;
+
+ /*
+ Load package name(s) containing location provider support.
+ These packages can contain services implementing location providers:
+ Geocoder Provider, Network Location Provider, and
+ Fused Location Provider. They will each be searched for
+ service components implementing these providers.
+ The location framework also has support for installation
+ of new location providers at run-time. The new package does not
+ have to be explicitly listed here, however it must have a signature
+ that matches the signature of at least one package on this list.
+ */
+ Resources resources = mContext.getResources();
+ ArrayList<String> providerPackageNames = new ArrayList<String>();
+ String[] pkgs1 = resources.getStringArray(
+ com.android.internal.R.array.config_locationProviderPackageNames);
+ String[] pkgs2 = resources.getStringArray(
+ com.android.internal.R.array.config_overlay_locationProviderPackageNames);
+ if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
+ if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
+ if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
+ if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
+
+ // bind to network provider
+ LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
+ mContext,
+ LocationManager.NETWORK_PROVIDER,
+ NETWORK_LOCATION_SERVICE_ACTION,
+ providerPackageNames, mLocationHandler);
+ if (networkProvider != null) {
+ mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+ mProxyProviders.add(networkProvider);
+ addProviderLocked(networkProvider);
+ } else {
+ Slog.w(TAG, "no network location provider found");
+ }
+
+ // bind to fused provider
+ LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+ mContext,
+ LocationManager.FUSED_PROVIDER,
+ FUSED_LOCATION_SERVICE_ACTION,
+ providerPackageNames, mLocationHandler);
+ if (fusedLocationProvider != null) {
+ addProviderLocked(fusedLocationProvider);
+ mProxyProviders.add(fusedLocationProvider);
+ mEnabledProviders.add(fusedLocationProvider.getName());
+ } else {
+ Slog.e(TAG, "no fused location provider found",
+ new IllegalStateException("Location service needs a fused location provider"));
+ }
- // for prefix blacklist
- private String[] mWhitelist = new String[0];
- private String[] mBlacklist = new String[0];
+ // bind to geocoder provider
+ mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
+ if (mGeocodeProvider == null) {
+ Slog.e(TAG, "no geocoder provider found");
+ }
- // for Settings change notification
- private ContentQueryMap mSettings;
+ updateProvidersLocked();
+ }
/**
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
*/
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
+ final int mUid; // uid of receiver
+ final int mPid; // pid of receiver
+ final String mPackageName; // package name of receiver
+ final String mPermission; // best permission that receiver has
+
final ILocationListener mListener;
final PendingIntent mPendingIntent;
final Object mKey;
+
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
- final String mPackageName;
int mPendingBroadcasts;
- String mRequiredPermissions;
- Receiver(ILocationListener listener, String packageName) {
+ Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+ String packageName) {
mListener = listener;
- mPendingIntent = null;
- mKey = listener.asBinder();
- mPackageName = packageName;
- }
-
- Receiver(PendingIntent intent, String packageName) {
mPendingIntent = intent;
- mListener = null;
- mKey = intent;
+ if (listener != null) {
+ mKey = listener.asBinder();
+ } else {
+ mKey = intent;
+ }
+ mPermission = checkPermission();
+ mUid = uid;
+ mPid = pid;
mPackageName = packageName;
}
@Override
public boolean equals(Object otherObj) {
if (otherObj instanceof Receiver) {
- return mKey.equals(
- ((Receiver)otherObj).mKey);
+ return mKey.equals(((Receiver)otherObj).mKey);
}
return false;
}
@@ -250,18 +350,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public String toString() {
- String result;
+ StringBuilder s = new StringBuilder();
+ s.append("Reciever[");
+ s.append(Integer.toHexString(System.identityHashCode(this)));
if (mListener != null) {
- result = "Receiver{"
- + Integer.toHexString(System.identityHashCode(this))
- + " Listener " + mKey + "}";
+ s.append(" listener");
} else {
- result = "Receiver{"
- + Integer.toHexString(System.identityHashCode(this))
- + " Intent " + mKey + "}";
+ s.append(" intent");
}
- result += "mUpdateRecords: " + mUpdateRecords;
- return result;
+ for (String p : mUpdateRecords.keySet()) {
+ s.append(" ").append(mUpdateRecords.get(p).toString());
+ }
+ s.append("]");
+ return s.toString();
}
public boolean isListener() {
@@ -279,13 +380,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new IllegalStateException("Request for non-existent listener");
}
- public PendingIntent getPendingIntent() {
- if (mPendingIntent != null) {
- return mPendingIntent;
- }
- throw new IllegalStateException("Request for non-existent intent");
- }
-
public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
if (mListener != null) {
try {
@@ -293,11 +387,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mListener.onStatusChanged(provider, status, extras);
- if (mListener != mProximityListener) {
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ // call this after broadcasting so we do not increment
+ // if we throw an exeption.
+ incrementPendingBroadcastsLocked();
}
} catch (RemoteException e) {
return false;
@@ -311,7 +403,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -330,11 +422,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mListener.onLocationChanged(location);
- if (mListener != mProximityListener) {
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ // call this after broadcasting so we do not increment
+ // if we throw an exeption.
+ incrementPendingBroadcastsLocked();
}
} catch (RemoteException e) {
return false;
@@ -347,7 +437,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -370,11 +460,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
} else {
mListener.onProviderDisabled(provider);
}
- if (mListener != mProximityListener) {
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ // call this after broadcasting so we do not increment
+ // if we throw an exeption.
+ incrementPendingBroadcastsLocked();
}
} catch (RemoteException e) {
return false;
@@ -387,7 +475,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -401,9 +489,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public void binderDied() {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Location listener died");
- }
+ if (D) Log.d(TAG, "Location listener died");
+
synchronized (mLock) {
removeUpdatesLocked(this);
}
@@ -415,6 +502,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void onSendFinished(PendingIntent pendingIntent, Intent intent,
int resultCode, String resultData, Bundle resultExtras) {
synchronized (this) {
@@ -437,6 +525,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void locationCallbackFinished(ILocationListener listener) {
//Do not use getReceiver here as that will add the ILocationListener to
//the receiver list if it is not found. If it is not found then the
@@ -454,198 +543,25 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private final class SettingsObserver implements Observer {
- public void update(Observable o, Object arg) {
- synchronized (mLock) {
- updateProvidersLocked();
- }
+ /** Settings Observer callback */
+ @Override
+ public void update(Observable o, Object arg) {
+ synchronized (mLock) {
+ updateProvidersLocked();
}
}
- private void addProvider(LocationProviderInterface provider) {
+ private void addProviderLocked(LocationProviderInterface provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProvider(LocationProviderInterface provider) {
+ private void removeProviderLocked(LocationProviderInterface provider) {
+ provider.disable();
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
- private void loadProviders() {
- synchronized (mLock) {
- if (sProvidersLoaded) {
- return;
- }
-
- // Load providers
- loadProvidersLocked();
- sProvidersLoaded = true;
- }
- }
-
- private void loadProvidersLocked() {
- try {
- _loadProvidersLocked();
- } catch (Exception e) {
- Slog.e(TAG, "Exception loading providers:", e);
- }
- }
-
- private void _loadProvidersLocked() {
- // Attempt to load "real" providers first
- if (GpsLocationProvider.isSupported()) {
- // Create a gps location provider
- GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
- mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
- mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
- addProvider(gpsProvider);
- mGpsLocationProvider = gpsProvider;
- }
-
- // create a passive location provider, which is always enabled
- PassiveProvider passiveProvider = new PassiveProvider(this);
- addProvider(passiveProvider);
- mEnabledProviders.add(passiveProvider.getName());
-
- // initialize external network location and geocoder services.
- // The initial value of mNetworkLocationProviderPackageName and
- // mGeocodeProviderPackageName is just used to determine what
- // signatures future mNetworkLocationProviderPackageName and
- // mGeocodeProviderPackageName packages must have. So alternate
- // providers can be installed under a different package name
- // so long as they have the same signature as the original
- // provider packages.
- if (mNetworkLocationProviderPackageName != null) {
- String packageName = findBestPackage(LocationProviderProxy.SERVICE_ACTION,
- mNetworkLocationProviderPackageName);
- if (packageName != null) {
- mNetworkLocationProvider = new LocationProviderProxy(mContext,
- LocationManager.NETWORK_PROVIDER,
- packageName, mLocationHandler);
- mNetworkLocationProviderPackageName = packageName;
- addProvider(mNetworkLocationProvider);
- }
- }
- if (mGeocodeProviderPackageName != null) {
- String packageName = findBestPackage(GeocoderProxy.SERVICE_ACTION,
- mGeocodeProviderPackageName);
- if (packageName != null) {
- mGeocodeProvider = new GeocoderProxy(mContext, packageName);
- mGeocodeProviderPackageName = packageName;
- }
- }
-
- updateProvidersLocked();
- }
-
- /**
- * Pick the best (network location provider or geocode provider) package.
- * The best package:
- * - implements serviceIntentName
- * - has signatures that match that of sigPackageName
- * - has the highest version value in a meta-data field in the service component
- */
- String findBestPackage(String serviceIntentName, String sigPackageName) {
- Intent intent = new Intent(serviceIntentName);
- List<ResolveInfo> infos = mPackageManager.queryIntentServices(intent,
- PackageManager.GET_META_DATA);
- if (infos == null) return null;
-
- int bestVersion = Integer.MIN_VALUE;
- String bestPackage = null;
- for (ResolveInfo info : infos) {
- String packageName = info.serviceInfo.packageName;
- // check signature
- if (mPackageManager.checkSignatures(packageName, sigPackageName) !=
- PackageManager.SIGNATURE_MATCH) {
- Slog.w(TAG, packageName + " implements " + serviceIntentName +
- " but its signatures don't match those in " + sigPackageName +
- ", ignoring");
- continue;
- }
- // read version
- int version = 0;
- if (info.serviceInfo.metaData != null) {
- version = info.serviceInfo.metaData.getInt("version", 0);
- }
- if (LOCAL_LOGV) Slog.v(TAG, packageName + " implements " + serviceIntentName +
- " with version " + version);
- if (version > bestVersion) {
- bestVersion = version;
- bestPackage = packageName;
- }
- }
-
- return bestPackage;
- }
-
- /**
- * @param context the context that the LocationManagerService runs in
- */
- public LocationManagerService(Context context) {
- super();
- mContext = context;
- Resources resources = context.getResources();
-
- mNetworkLocationProviderPackageName = resources.getString(
- com.android.internal.R.string.config_networkLocationProviderPackageName);
- mGeocodeProviderPackageName = resources.getString(
- com.android.internal.R.string.config_geocodeProviderPackageName);
-
- mPackageMonitor.register(context, null, true);
-
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Constructed LocationManager Service");
- }
- }
-
- void systemReady() {
- // we defer starting up the service until the system is ready
- Thread thread = new Thread(null, this, "LocationManagerService");
- thread.start();
- }
-
- private void initialize() {
- // Create a wake lock, needs to be done before calling loadProviders() below
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- mPackageManager = mContext.getPackageManager();
-
- // Load providers
- loadProviders();
- loadBlacklist();
-
- // Register for Network (Wifi or Mobile) updates
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- // Register for Package Manager updates
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
-
- // listen for settings changes
- ContentResolver resolver = mContext.getContentResolver();
- Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
- "(" + Settings.System.NAME + "=?)",
- new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
- null);
- mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
- SettingsObserver settingsObserver = new SettingsObserver();
- mSettings.addObserver(settingsObserver);
- }
-
- public void run()
- {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- Looper.prepare();
- mLocationHandler = new LocationWorkerHandler();
- initialize();
- Looper.loop();
- }
private boolean isAllowedBySettingsLocked(String provider) {
if (mEnabledProviders.contains(provider)) {
@@ -660,317 +576,131 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return Settings.Secure.isLocationProviderEnabled(resolver, provider);
}
- private String checkPermissionsSafe(String provider, String lastPermission) {
- if (LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider)) {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION permission");
- }
- return ACCESS_FINE_LOCATION;
- }
-
- // Assume any other provider requires the coarse or fine permission.
- if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
- return ACCESS_FINE_LOCATION.equals(lastPermission)
- ? lastPermission : ACCESS_COARSE_LOCATION;
- }
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
+ /**
+ * Throw SecurityException if caller has neither COARSE or FINE.
+ * Otherwise, return the best permission.
+ */
+ private String checkPermission() {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED) {
return ACCESS_FINE_LOCATION;
+ } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED) {
+ return ACCESS_COARSE_LOCATION;
}
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
- }
-
- private boolean isAllowedProviderSafe(String provider) {
- if ((LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider))
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
- }
- if (LocationManager.NETWORK_PROVIDER.equals(provider)
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)
- && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
- }
-
- return true;
+ throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
+ "ACCESS_FINE_LOCATION permission");
}
+ /**
+ * Returns all providers by name, including passive, but excluding
+ * fused.
+ */
+ @Override
public List<String> getAllProviders() {
- try {
- synchronized (mLock) {
- return _getAllProvidersLocked();
+ checkPermission();
+
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<String>(mProviders.size());
+ for (LocationProviderInterface provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
+ out.add(name);
}
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getAllProviders got exception:", e);
- return null;
}
- }
- private List<String> _getAllProvidersLocked() {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getAllProviders");
- }
- ArrayList<String> out = new ArrayList<String>(mProviders.size());
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- out.add(p.getName());
- }
+ if (D) Log.d(TAG, "getAllProviders()=" + out);
return out;
}
+ /**
+ * Return all providers by name, that match criteria and are optionally
+ * enabled.
+ * Can return passive provider, but never returns fused provider.
+ */
+ @Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
- try {
- synchronized (mLock) {
- return _getProvidersLocked(criteria, enabledOnly);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getProviders got exception:", e);
- return null;
- }
- }
+ checkPermission();
- private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getProviders");
- }
- ArrayList<String> out = new ArrayList<String>(mProviders.size());
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- String name = p.getName();
- if (isAllowedProviderSafe(name)) {
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<String>(mProviders.size());
+ for (LocationProviderInterface provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
if (enabledOnly && !isAllowedBySettingsLocked(name)) {
continue;
}
- if (criteria != null && !p.meetsCriteria(criteria)) {
+ if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+ name, provider.getProperties(), criteria)) {
continue;
}
out.add(name);
}
}
+
+ if (D) Log.d(TAG, "getProviders()=" + out);
return out;
}
/**
- * Returns the next looser power requirement, in the sequence:
- *
- * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
+ * Return the name of the best provider given a Criteria object.
+ * This method has been deprecated from the public API,
+ * and the whole LoactionProvider (including #meetsCriteria)
+ * has been deprecated as well. So this method now uses
+ * some simplified logic.
*/
- private int nextPower(int power) {
- switch (power) {
- case Criteria.POWER_LOW:
- return Criteria.POWER_MEDIUM;
- case Criteria.POWER_MEDIUM:
- return Criteria.POWER_HIGH;
- case Criteria.POWER_HIGH:
- return Criteria.NO_REQUIREMENT;
- case Criteria.NO_REQUIREMENT:
- default:
- return Criteria.NO_REQUIREMENT;
- }
- }
+ @Override
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ String result = null;
+ checkPermission();
- /**
- * Returns the next looser accuracy requirement, in the sequence:
- *
- * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
- */
- private int nextAccuracy(int accuracy) {
- if (accuracy == Criteria.ACCURACY_FINE) {
- return Criteria.ACCURACY_COARSE;
- } else {
- return Criteria.NO_REQUIREMENT;
+ List<String> providers = getProviders(criteria, enabledOnly);
+ if (providers.size() < 1) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
- }
-
- private class LpPowerComparator implements Comparator<LocationProviderInterface> {
- public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
- // Smaller is better
- return (l1.getPowerRequirement() - l2.getPowerRequirement());
- }
-
- public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (l1.getPowerRequirement() == l2.getPowerRequirement());
- }
- }
-
- private class LpAccuracyComparator implements Comparator<LocationProviderInterface> {
- public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
- // Smaller is better
- return (l1.getAccuracy() - l2.getAccuracy());
- }
-
- public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (l1.getAccuracy() == l2.getAccuracy());
- }
- }
-
- private class LpCapabilityComparator implements Comparator<LocationProviderInterface> {
-
- private static final int ALTITUDE_SCORE = 4;
- private static final int BEARING_SCORE = 4;
- private static final int SPEED_SCORE = 4;
-
- private int score(LocationProviderInterface p) {
- return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
- (p.supportsBearing() ? BEARING_SCORE : 0) +
- (p.supportsSpeed() ? SPEED_SCORE : 0);
+ providers = getProviders(null, enabledOnly);
+ if (providers.size() >= 1) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
- public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (score(l2) - score(l1)); // Bigger is better
- }
-
- public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (score(l1) == score(l2));
- }
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return null;
}
- private LocationProviderInterface best(List<String> providerNames) {
- ArrayList<LocationProviderInterface> providers;
- synchronized (mLock) {
- providers = new ArrayList<LocationProviderInterface>(providerNames.size());
- for (String name : providerNames) {
- providers.add(mProvidersByName.get(name));
- }
- }
-
- if (providers.size() < 2) {
- return providers.get(0);
- }
-
- // First, sort by power requirement
- Collections.sort(providers, new LpPowerComparator());
- int power = providers.get(0).getPowerRequirement();
- if (power < providers.get(1).getPowerRequirement()) {
+ private String pickBest(List<String> providers) {
+ if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+ return LocationManager.NETWORK_PROVIDER;
+ } else if (providers.contains(LocationManager.GPS_PROVIDER)) {
+ return LocationManager.GPS_PROVIDER;
+ } else {
return providers.get(0);
}
-
- int idx, size;
-
- ArrayList<LocationProviderInterface> tmp = new ArrayList<LocationProviderInterface>();
- idx = 0;
- size = providers.size();
- while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
- tmp.add(providers.get(idx));
- idx++;
- }
-
- // Next, sort by accuracy
- Collections.sort(tmp, new LpAccuracyComparator());
- int acc = tmp.get(0).getAccuracy();
- if (acc < tmp.get(1).getAccuracy()) {
- return tmp.get(0);
- }
-
- ArrayList<LocationProviderInterface> tmp2 = new ArrayList<LocationProviderInterface>();
- idx = 0;
- size = tmp.size();
- while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
- tmp2.add(tmp.get(idx));
- idx++;
- }
-
- // Finally, sort by capability "score"
- Collections.sort(tmp2, new LpCapabilityComparator());
- return tmp2.get(0);
- }
-
- /**
- * Returns the name of the provider that best meets the given criteria. Only providers
- * that are permitted to be accessed by the calling activity will be
- * returned. If several providers meet the criteria, the one with the best
- * accuracy is returned. If no provider meets the criteria,
- * the criteria are loosened in the following sequence:
- *
- * <ul>
- * <li> power requirement
- * <li> accuracy
- * <li> bearing
- * <li> speed
- * <li> altitude
- * </ul>
- *
- * <p> Note that the requirement on monetary cost is not removed
- * in this process.
- *
- * @param criteria the criteria that need to be matched
- * @param enabledOnly if true then only a provider that is currently enabled is returned
- * @return name of the provider that best matches the requirements
- */
- public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- List<String> goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Make a copy of the criteria that we can modify
- criteria = new Criteria(criteria);
-
- // Loosen power requirement
- int power = criteria.getPowerRequirement();
- while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
- power = nextPower(power);
- criteria.setPowerRequirement(power);
- goodProviders = getProviders(criteria, enabledOnly);
- }
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Loosen accuracy requirement
- int accuracy = criteria.getAccuracy();
- while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
- accuracy = nextAccuracy(accuracy);
- criteria.setAccuracy(accuracy);
- goodProviders = getProviders(criteria, enabledOnly);
- }
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Remove bearing requirement
- criteria.setBearingRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Remove speed requirement
- criteria.setSpeedRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Remove altitude requirement
- criteria.setAltitudeRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- return null;
}
+ @Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ checkPermission();
+
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
}
- return p.meetsCriteria(criteria);
+
+ boolean result = LocationProvider.propertiesMeetCriteria(
+ p.getName(), p.getProperties(), criteria);
+ if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
+ return result;
}
private void updateProvidersLocked() {
@@ -997,16 +727,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
int listeners = 0;
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return;
- }
+ if (p == null) return;
ArrayList<Receiver> deadReceivers = null;
-
+
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records != null) {
final int N = records.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
UpdateRecord record = records.get(i);
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
@@ -1020,67 +748,74 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
if (deadReceivers != null) {
- for (int i=deadReceivers.size()-1; i>=0; i--) {
+ for (int i = deadReceivers.size() - 1; i >= 0; i--) {
removeUpdatesLocked(deadReceivers.get(i));
}
}
-
+
if (enabled) {
p.enable();
if (listeners > 0) {
- p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
- p.enableLocationTracking(true);
+ applyRequirementsLocked(provider);
}
} else {
- p.enableLocationTracking(false);
p.disable();
}
}
- private long getMinTimeLocked(String provider) {
- long minTime = Long.MAX_VALUE;
+ private void applyRequirementsLocked(String provider) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) return;
+
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- mTmpWorkSource.clear();
+ WorkSource worksource = new WorkSource();
+ ProviderRequest providerRequest = new ProviderRequest();
+
if (records != null) {
- for (int i=records.size()-1; i>=0; i--) {
- UpdateRecord ur = records.get(i);
- long curTime = ur.mMinTime;
- if (curTime < minTime) {
- minTime = curTime;
+ for (UpdateRecord record : records) {
+ LocationRequest locationRequest = record.mRequest;
+
+ providerRequest.locationRequests.add(locationRequest);
+ if (locationRequest.getInterval() < providerRequest.interval) {
+ providerRequest.reportLocation = true;
+ providerRequest.interval = locationRequest.getInterval();
}
}
- long inclTime = (minTime*3)/2;
- for (int i=records.size()-1; i>=0; i--) {
- UpdateRecord ur = records.get(i);
- if (ur.mMinTime <= inclTime) {
- mTmpWorkSource.add(ur.mUid);
+
+ if (providerRequest.reportLocation) {
+ // calculate who to blame for power
+ // This is somewhat arbitrary. We pick a threshold interval
+ // that is slightly higher that the minimum interval, and
+ // spread the blame across all applications with a request
+ // under that threshold.
+ long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
+ for (UpdateRecord record : records) {
+ LocationRequest locationRequest = record.mRequest;
+ if (locationRequest.getInterval() <= thresholdInterval) {
+ worksource.add(record.mReceiver.mUid);
+ }
}
}
}
- return minTime;
+
+ if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
+ p.setRequest(providerRequest, worksource);
}
private class UpdateRecord {
final String mProvider;
+ final LocationRequest mRequest;
final Receiver mReceiver;
- final long mMinTime;
- final float mMinDistance;
- final boolean mSingleShot;
- final int mUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot,
- Receiver receiver, int uid) {
+ UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
mProvider = provider;
+ mRequest = request;
mReceiver = receiver;
- mMinTime = minTime;
- mMinDistance = minDistance;
- mSingleShot = singleShot;
- mUid = uid;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
@@ -1096,45 +831,49 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
* Method to be called when a record will no longer be used. Calling this multiple times
* must have the same effect as calling it once.
*/
- void disposeLocked() {
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
- if (records != null) {
- records.remove(this);
+ void disposeLocked(boolean removeReceiver) {
+ // remove from mRecordsByProvider
+ ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
+ if (globalRecords != null) {
+ globalRecords.remove(this);
+ }
+
+ if (!removeReceiver) return; // the caller will handle the rest
+
+ // remove from Receiver#mUpdateRecords
+ HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
+ if (receiverRecords != null) {
+ receiverRecords.remove(this.mProvider);
+
+ // and also remove the Receiver if it has no more update records
+ if (removeReceiver && receiverRecords.size() == 0) {
+ removeUpdatesLocked(mReceiver);
+ }
}
}
@Override
public String toString() {
- return "UpdateRecord{"
- + Integer.toHexString(System.identityHashCode(this))
- + " mProvider: " + mProvider + " mUid: " + mUid + "}";
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
- pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
- pw.println(prefix + "mSingleShot=" + mSingleShot);
- pw.println(prefix + "mUid=" + mUid);
- pw.println(prefix + "mLastFixBroadcast:");
- if (mLastFixBroadcast != null) {
- mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
- }
- pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
+ StringBuilder s = new StringBuilder();
+ s.append("UpdateRecord[");
+ s.append(mProvider);
+ s.append(' ').append(mReceiver.mPackageName).append('(');
+ s.append(mReceiver.mUid).append(')');
+ s.append(' ').append(mRequest);
+ s.append(']');
+ return s.toString();
}
}
- private Receiver getReceiver(ILocationListener listener, String packageName) {
+ private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
- receiver = new Receiver(listener, packageName);
+ receiver = new Receiver(listener, null, pid, uid, packageName);
mReceivers.put(binder, receiver);
try {
- if (receiver.isListener()) {
- receiver.getListener().asBinder().linkToDeath(receiver, 0);
- }
+ receiver.getListener().asBinder().linkToDeath(receiver, 0);
} catch (RemoteException e) {
Slog.e(TAG, "linkToDeath failed:", e);
return null;
@@ -1143,251 +882,256 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return receiver;
}
- private Receiver getReceiver(PendingIntent intent, String packageName) {
+ private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
- receiver = new Receiver(intent, packageName);
+ receiver = new Receiver(null, intent, pid, uid, packageName);
mReceivers.put(intent, receiver);
}
return receiver;
}
- private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records != null) {
- for (int i = records.size() - 1; i >= 0; i--) {
- UpdateRecord record = records.get(i);
- if (record.mUid == uid && record.mReceiver != excludedReceiver) {
- return true;
- }
- }
+ private String checkPermissionAndRequest(LocationRequest request) {
+ String perm = checkPermission();
+
+ if (ACCESS_COARSE_LOCATION.equals(perm)) {
+ switch (request.getQuality()) {
+ case LocationRequest.ACCURACY_FINE:
+ request.setQuality(LocationRequest.ACCURACY_BLOCK);
+ break;
+ case LocationRequest.POWER_HIGH:
+ request.setQuality(LocationRequest.POWER_LOW);
+ break;
+ }
+ // throttle
+ if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+ request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
+ }
+ if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+ request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
+ }
}
- for (ProximityAlert alert : mProximityAlerts.values()) {
- if (alert.mUid == uid) {
- return true;
- }
+ // make getFastestInterval() the minimum of interval and fastest interval
+ if (request.getFastestInterval() > request.getInterval()) {
+ request.setFastestInterval(request.getInterval());
}
- return false;
+ return perm;
}
- public void requestLocationUpdates(String provider, Criteria criteria,
- long minTime, float minDistance, boolean singleShot, ILocationListener listener,
- String packageName) {
- checkPackageName(Binder.getCallingUid(), packageName);
- if (criteria != null) {
- // FIXME - should we consider using multiple providers simultaneously
- // rather than only the best one?
- // Should we do anything different for single shot fixes?
- provider = getBestProvider(criteria, true);
- if (provider == null) {
- throw new IllegalArgumentException("no providers found for criteria");
- }
+ private void checkPackageName(String packageName) {
+ if (packageName == null) {
+ throw new SecurityException("invalid package name: " + packageName);
}
- try {
- synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
- getReceiver(listener, packageName));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "requestUpdates got exception:", e);
+ int uid = Binder.getCallingUid();
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages == null) {
+ throw new SecurityException("invalid UID " + uid);
+ }
+ for (String pkg : packages) {
+ if (packageName.equals(pkg)) return;
}
+ throw new SecurityException("invalid package name: " + packageName);
}
- void validatePendingIntent(PendingIntent intent) {
- if (intent.isTargetedToPackage()) {
- return;
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + intent);
}
- Slog.i(TAG, "Given Intent does not require a specific package: "
- + intent);
- // XXX we should really throw a security exception, if the caller's
- // targetSdkVersion is high enough.
- //throw new SecurityException("Given Intent does not require a specific package: "
- // + intent);
}
- public void requestLocationUpdatesPI(String provider, Criteria criteria,
- long minTime, float minDistance, boolean singleShot, PendingIntent intent,
- String packageName) {
- checkPackageName(Binder.getCallingUid(), packageName);
- validatePendingIntent(intent);
- if (criteria != null) {
- // FIXME - should we consider using multiple providers simultaneously
- // rather than only the best one?
- // Should we do anything different for single shot fixes?
- provider = getBestProvider(criteria, true);
- if (provider == null) {
- throw new IllegalArgumentException("no providers found for criteria");
- }
- }
- try {
- synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
- getReceiver(intent, packageName));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "requestUpdates got exception:", e);
+ private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
+ int pid, int uid, String packageName) {
+ if (intent == null && listener == null) {
+ throw new IllegalArgumentException("need eiter listener or intent");
+ } else if (intent != null && listener != null) {
+ throw new IllegalArgumentException("cannot register both listener and intent");
+ } else if (intent != null) {
+ checkPendingIntent(intent);
+ return getReceiver(intent, pid, uid, packageName);
+ } else {
+ return getReceiver(listener, pid, uid, packageName);
}
}
- private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance,
- boolean singleShot, Receiver receiver) {
+ @Override
+ public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
+ PendingIntent intent, String packageName) {
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ checkPackageName(packageName);
+ checkPermissionAndRequest(request);
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- throw new IllegalArgumentException("requested provider " + provider +
- " doesn't exisit");
- }
- receiver.mRequiredPermissions = checkPermissionsSafe(provider,
- receiver.mRequiredPermissions);
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
- // so wakelock calls will succeed
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
- boolean newUid = !providerHasListener(provider, callingUid, null);
+ // providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
- UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot,
- receiver, callingUid);
- UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
- if (oldRecord != null) {
- oldRecord.disposeLocked();
- }
-
- if (newUid) {
- p.addListener(callingUid);
- }
-
- boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
- if (isProviderEnabled) {
- long minTimeForProvider = getMinTimeLocked(provider);
- Slog.i(TAG, "request " + provider + " (pid " + callingPid + ") " + minTime +
- " " + minTimeForProvider + (singleShot ? " (singleshot)" : ""));
- p.setMinTime(minTimeForProvider, mTmpWorkSource);
- // try requesting single shot if singleShot is true, and fall back to
- // regular location tracking if requestSingleShotFix() is not supported
- if (!singleShot || !p.requestSingleShotFix()) {
- p.enableLocationTracking(true);
- }
- } else {
- // Notify the listener that updates are currently disabled
- receiver.callProviderEnabledLocked(provider, false);
- }
- if (LOCAL_LOGV) {
- Slog.v(TAG, "_requestLocationUpdates: provider = " + provider + " listener = " + receiver);
+ synchronized (mLock) {
+ requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- public void removeUpdates(ILocationListener listener, String packageName) {
- try {
- synchronized (mLock) {
- removeUpdatesLocked(getReceiver(listener, packageName));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeUpdates got exception:", e);
+ private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
+ int pid, int uid, String packageName) {
+ // Figure out the provider. Either its explicitly request (legacy use cases), or
+ // use the fused provider
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProviderInterface provider = mProvidersByName.get(name);
+ if (provider == null) {
+ throw new IllegalArgumentException("provider doesn't exisit: " + provider);
+ }
+
+ Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
+ name + " " + request + " from " + packageName + "(" + uid + ")");
+
+ UpdateRecord record = new UpdateRecord(name, request, receiver);
+ UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
+ if (oldRecord != null) {
+ oldRecord.disposeLocked(false);
+ }
+
+ boolean isProviderEnabled = isAllowedBySettingsLocked(name);
+ if (isProviderEnabled) {
+ applyRequirementsLocked(name);
+ } else {
+ // Notify the listener that updates are currently disabled
+ receiver.callProviderEnabledLocked(name, false);
}
}
- public void removeUpdatesPI(PendingIntent intent, String packageName) {
+ @Override
+ public void removeUpdates(ILocationListener listener, PendingIntent intent,
+ String packageName) {
+ checkPackageName(packageName);
+ checkPermission();
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
+
+ // providers may use public location API's, need to clear identity
+ long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- removeUpdatesLocked(getReceiver(intent, packageName));
+ removeUpdatesLocked(receiver);
}
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeUpdates got exception:", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
private void removeUpdatesLocked(Receiver receiver) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "_removeUpdates: listener = " + receiver);
- }
+ Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
- // so wakelock calls will succeed
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
- receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
- synchronized(receiver) {
- if(receiver.mPendingBroadcasts > 0) {
- decrementPendingBroadcasts();
- receiver.mPendingBroadcasts = 0;
- }
+ if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
+ receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
+ synchronized (receiver) {
+ if (receiver.mPendingBroadcasts > 0) {
+ decrementPendingBroadcasts();
+ receiver.mPendingBroadcasts = 0;
}
}
+ }
- // Record which providers were associated with this listener
- HashSet<String> providers = new HashSet<String>();
- HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
- if (oldRecords != null) {
- // Call dispose() on the obsolete update records.
- for (UpdateRecord record : oldRecords.values()) {
- if (!providerHasListener(record.mProvider, callingUid, receiver)) {
- LocationProviderInterface p = mProvidersByName.get(record.mProvider);
- if (p != null) {
- p.removeListener(callingUid);
- }
- }
- record.disposeLocked();
- }
- // Accumulate providers
- providers.addAll(oldRecords.keySet());
+ // Record which providers were associated with this listener
+ HashSet<String> providers = new HashSet<String>();
+ HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
+ if (oldRecords != null) {
+ // Call dispose() on the obsolete update records.
+ for (UpdateRecord record : oldRecords.values()) {
+ record.disposeLocked(false);
}
+ // Accumulate providers
+ providers.addAll(oldRecords.keySet());
+ }
- // See if the providers associated with this listener have any
- // other listeners; if one does, inform it of the new smallest minTime
- // value; if one does not, disable location tracking for it
- for (String provider : providers) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedBySettingsLocked(provider)) {
- continue;
- }
+ // update provider
+ for (String provider : providers) {
+ // If provider is already disabled, don't need to do anything
+ if (!isAllowedBySettingsLocked(provider)) {
+ continue;
+ }
- boolean hasOtherListener = false;
- ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
- if (recordsForProvider != null && recordsForProvider.size() > 0) {
- hasOtherListener = true;
- }
+ applyRequirementsLocked(provider);
+ }
+ }
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p != null) {
- if (hasOtherListener) {
- long minTime = getMinTimeLocked(provider);
- Slog.i(TAG, "remove " + provider + " (pid " + callingPid +
- "), next minTime = " + minTime);
- p.setMinTime(minTime, mTmpWorkSource);
- } else {
- Slog.i(TAG, "remove " + provider + " (pid " + callingPid +
- "), disabled");
- p.enableLocationTracking(false);
- }
- }
+ @Override
+ public Location getLastLocation(LocationRequest request, String packageName) {
+ if (D) Log.d(TAG, "getLastLocation: " + request);
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ String perm = checkPermissionAndRequest(request);
+ checkPackageName(packageName);
+
+ if (mBlacklist.isBlacklisted(packageName)) {
+ if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
+ packageName);
+ return null;
+ }
+
+ synchronized (mLock) {
+ // Figure out the provider. Either its explicitly request (deprecated API's),
+ // or use the fused provider
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProviderInterface provider = mProvidersByName.get(name);
+ if (provider == null) return null;
+
+ if (!isAllowedBySettingsLocked(name)) return null;
+
+ Location location = mLastLocation.get(name);
+ if (ACCESS_FINE_LOCATION.equals(perm)) {
+ return location;
+ } else {
+ return mLocationFudger.getOrCreate(location);
}
+ }
+ }
+
+ @Override
+ public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
+ String packageName) {
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ checkPermissionAndRequest(request);
+ checkPendingIntent(intent);
+ checkPackageName(packageName);
+
+ if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
+
+ // geo-fence manager uses the public location API, need to clear identity
+ int uid = Binder.getCallingUid();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
+ checkPermission();
+ checkPendingIntent(intent);
+ checkPackageName(packageName);
+
+ if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
+
+ // geo-fence manager uses the public location API, need to clear identity
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mGeofenceManager.removeFence(geofence, intent);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
public boolean addGpsStatusListener(IGpsStatusListener listener) {
if (mGpsStatusProvider == null) {
return false;
@@ -1406,6 +1150,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return true;
}
+ @Override
public void removeGpsStatusListener(IGpsStatusListener listener) {
synchronized (mLock) {
try {
@@ -1416,14 +1161,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
if (provider == null) {
// throw NullPointerException to remain compatible with previous implementation
throw new NullPointerException();
}
- // first check for permission to the provider
- checkPermissionsSafe(provider, null);
+ checkPermission();
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -1432,424 +1177,113 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (mLock) {
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return false;
- }
-
+ if (p == null) return false;
+
return p.sendExtraCommand(command, extras);
}
}
- public boolean sendNiResponse(int notifId, int userResponse)
- {
+ @Override
+ public boolean sendNiResponse(int notifId, int userResponse) {
if (Binder.getCallingUid() != Process.myUid()) {
throw new SecurityException(
"calling sendNiResponse from outside of the system is not allowed");
}
try {
return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
- }
- catch (RemoteException e)
- {
+ } catch (RemoteException e) {
Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
return false;
}
}
- class ProximityAlert {
- final int mUid;
- final double mLatitude;
- final double mLongitude;
- final float mRadius;
- final long mExpiration;
- final PendingIntent mIntent;
- final Location mLocation;
- final String mPackageName;
-
- public ProximityAlert(int uid, double latitude, double longitude,
- float radius, long expiration, PendingIntent intent, String packageName) {
- mUid = uid;
- mLatitude = latitude;
- mLongitude = longitude;
- mRadius = radius;
- mExpiration = expiration;
- mIntent = intent;
- mPackageName = packageName;
-
- mLocation = new Location("");
- mLocation.setLatitude(latitude);
- mLocation.setLongitude(longitude);
- }
-
- long getExpiration() {
- return mExpiration;
- }
-
- PendingIntent getIntent() {
- return mIntent;
- }
-
- boolean isInProximity(double latitude, double longitude, float accuracy) {
- Location loc = new Location("");
- loc.setLatitude(latitude);
- loc.setLongitude(longitude);
-
- double radius = loc.distanceTo(mLocation);
- return radius <= Math.max(mRadius,accuracy);
- }
-
- @Override
- public String toString() {
- return "ProximityAlert{"
- + Integer.toHexString(System.identityHashCode(this))
- + " uid " + mUid + mIntent + "}";
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
- pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
- pw.println(prefix + "mIntent=" + mIntent);
- pw.println(prefix + "mLocation:");
- mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- }
- }
-
- // Listener for receiving locations to trigger proximity alerts
- class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
-
- boolean isGpsAvailable = false;
-
- // Note: this is called with the lock held.
- public void onLocationChanged(Location loc) {
-
- // If Gps is available, then ignore updates from NetworkLocationProvider
- if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
- isGpsAvailable = true;
- }
- if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
- return;
- }
-
- // Process proximity alerts
- long now = System.currentTimeMillis();
- double latitude = loc.getLatitude();
- double longitude = loc.getLongitude();
- float accuracy = loc.getAccuracy();
- ArrayList<PendingIntent> intentsToRemove = null;
-
- for (ProximityAlert alert : mProximityAlerts.values()) {
- PendingIntent intent = alert.getIntent();
- long expiration = alert.getExpiration();
-
- if (inBlacklist(alert.mPackageName)) {
- continue;
- }
-
- if ((expiration == -1) || (now <= expiration)) {
- boolean entered = mProximitiesEntered.contains(alert);
- boolean inProximity =
- alert.isInProximity(latitude, longitude, accuracy);
- if (!entered && inProximity) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Entered alert");
- }
- mProximitiesEntered.add(alert);
- Intent enteredIntent = new Intent();
- enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcasts()
- // is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, enteredIntent, this, mLocationHandler,
- ACCESS_FINE_LOCATION);
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcasts();
- }
- } catch (PendingIntent.CanceledException e) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Canceled proximity alert: " + alert, e);
- }
- if (intentsToRemove == null) {
- intentsToRemove = new ArrayList<PendingIntent>();
- }
- intentsToRemove.add(intent);
- }
- } else if (entered && !inProximity) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Exited alert");
- }
- mProximitiesEntered.remove(alert);
- Intent exitedIntent = new Intent();
- exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcasts()
- // is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, exitedIntent, this, mLocationHandler,
- ACCESS_FINE_LOCATION);
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcasts();
- }
- } catch (PendingIntent.CanceledException e) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Canceled proximity alert: " + alert, e);
- }
- if (intentsToRemove == null) {
- intentsToRemove = new ArrayList<PendingIntent>();
- }
- intentsToRemove.add(intent);
- }
- }
- } else {
- // Mark alert for expiration
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Expiring proximity alert: " + alert);
- }
- if (intentsToRemove == null) {
- intentsToRemove = new ArrayList<PendingIntent>();
- }
- intentsToRemove.add(alert.getIntent());
- }
- }
-
- // Remove expired alerts
- if (intentsToRemove != null) {
- for (PendingIntent i : intentsToRemove) {
- ProximityAlert alert = mProximityAlerts.get(i);
- mProximitiesEntered.remove(alert);
- removeProximityAlertLocked(i);
- }
- }
- }
-
- // Note: this is called with the lock held.
- public void onProviderDisabled(String provider) {
- if (provider.equals(LocationManager.GPS_PROVIDER)) {
- isGpsAvailable = false;
- }
- }
-
- // Note: this is called with the lock held.
- public void onProviderEnabled(String provider) {
- // ignore
- }
-
- // Note: this is called with the lock held.
- public void onStatusChanged(String provider, int status, Bundle extras) {
- if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
- (status != LocationProvider.AVAILABLE)) {
- isGpsAvailable = false;
- }
- }
-
- public void onSendFinished(PendingIntent pendingIntent, Intent intent,
- int resultCode, String resultData, Bundle resultExtras) {
- // synchronize to ensure incrementPendingBroadcasts()
- // is called before decrementPendingBroadcasts()
- synchronized (this) {
- decrementPendingBroadcasts();
- }
- }
- }
-
- public void addProximityAlert(double latitude, double longitude,
- float radius, long expiration, PendingIntent intent, String packageName) {
- validatePendingIntent(intent);
- try {
- synchronized (mLock) {
- addProximityAlertLocked(latitude, longitude, radius, expiration, intent,
- packageName);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "addProximityAlert got exception:", e);
- }
- }
-
- private void addProximityAlertLocked(double latitude, double longitude,
- float radius, long expiration, PendingIntent intent, String packageName) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "addProximityAlert: latitude = " + latitude +
- ", longitude = " + longitude +
- ", expiration = " + expiration +
- ", intent = " + intent);
- }
-
- checkPackageName(Binder.getCallingUid(), packageName);
-
- // Require ability to access all providers for now
- if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
- !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
- throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
- }
-
- if (expiration != -1) {
- expiration += System.currentTimeMillis();
- }
- ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
- latitude, longitude, radius, expiration, intent, packageName);
- mProximityAlerts.put(intent, alert);
-
- if (mProximityReceiver == null) {
- mProximityListener = new ProximityListener();
- mProximityReceiver = new Receiver(mProximityListener, packageName);
-
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface provider = mProviders.get(i);
- requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f,
- false, mProximityReceiver);
- }
- }
- }
-
- public void removeProximityAlert(PendingIntent intent) {
- try {
- synchronized (mLock) {
- removeProximityAlertLocked(intent);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeProximityAlert got exception:", e);
- }
- }
-
- private void removeProximityAlertLocked(PendingIntent intent) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "removeProximityAlert: intent = " + intent);
- }
-
- mProximityAlerts.remove(intent);
- if (mProximityAlerts.size() == 0) {
- if (mProximityReceiver != null) {
- removeUpdatesLocked(mProximityReceiver);
- }
- mProximityReceiver = null;
- mProximityListener = null;
- }
- }
-
/**
* @return null if the provider does not exist
* @throws SecurityException if the provider is not allowed to be
* accessed by the caller
*/
- public Bundle getProviderInfo(String provider) {
- try {
- synchronized (mLock) {
- return _getProviderInfoLocked(provider);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "_getProviderInfo got exception:", e);
- return null;
- }
- }
+ @Override
+ public ProviderProperties getProviderProperties(String provider) {
+ checkPermission();
- private Bundle _getProviderInfoLocked(String provider) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return null;
+ LocationProviderInterface p;
+ synchronized (mLock) {
+ p = mProvidersByName.get(provider);
}
- checkPermissionsSafe(provider, null);
-
- Bundle b = new Bundle();
- b.putBoolean("network", p.requiresNetwork());
- b.putBoolean("satellite", p.requiresSatellite());
- b.putBoolean("cell", p.requiresCell());
- b.putBoolean("cost", p.hasMonetaryCost());
- b.putBoolean("altitude", p.supportsAltitude());
- b.putBoolean("speed", p.supportsSpeed());
- b.putBoolean("bearing", p.supportsBearing());
- b.putInt("power", p.getPowerRequirement());
- b.putInt("accuracy", p.getAccuracy());
-
- return b;
+ if (p == null) return null;
+ return p.getProperties();
}
+ @Override
public boolean isProviderEnabled(String provider) {
- try {
- synchronized (mLock) {
- return _isProviderEnabledLocked(provider);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "isProviderEnabled got exception:", e);
- return false;
+ checkPermission();
+ if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+
+ synchronized (mLock) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) return false;
+
+ return isAllowedBySettingsLocked(provider);
}
}
- public void reportLocation(Location location, boolean passive) {
+ private void checkCallerIsProvider() {
if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
}
- mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
- Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
- m.arg1 = (passive ? 1 : 0);
- mLocationHandler.sendMessageAtFrontOfQueue(m);
- }
+ // Previously we only used the INSTALL_LOCATION_PROVIDER
+ // check. But that is system or signature
+ // protection level which is not flexible enough for
+ // providers installed oustide the system image. So
+ // also allow providers with a UID matching the
+ // currently bound package name
- private boolean _isProviderEnabledLocked(String provider) {
- checkPermissionsSafe(provider, null);
+ int uid = Binder.getCallingUid();
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return false;
+ if (mGeocodeProvider != null) {
+ if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
}
- return isAllowedBySettingsLocked(provider);
+ for (LocationProviderProxy proxy : mProxyProviders) {
+ if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
+ }
+ throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
+ "or UID of a currently bound location provider");
}
- public Location getLastKnownLocation(String provider, String packageName) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getLastKnownLocation: " + provider);
+ private boolean doesPackageHaveUid(int uid, String packageName) {
+ if (packageName == null) {
+ return false;
}
try {
- synchronized (mLock) {
- return _getLastKnownLocationLocked(provider, packageName);
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
+ if (appInfo.uid != uid) {
+ return false;
}
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getLastKnownLocation got exception:", e);
- return null;
+ } catch (NameNotFoundException e) {
+ return false;
}
+ return true;
}
- private Location _getLastKnownLocationLocked(String provider, String packageName) {
- checkPermissionsSafe(provider, null);
- checkPackageName(Binder.getCallingUid(), packageName);
-
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return null;
- }
-
- if (!isAllowedBySettingsLocked(provider)) {
- return null;
- }
+ @Override
+ public void reportLocation(Location location, boolean passive) {
+ checkCallerIsProvider();
- if (inBlacklist(packageName)) {
- return null;
+ if (!location.isComplete()) {
+ Log.w(TAG, "Dropping incomplete location: " + location);
+ return;
}
- return mLastKnownLocation.get(provider);
+ mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
+ Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
+ m.arg1 = (passive ? 1 : 0);
+ mLocationHandler.sendMessageAtFrontOfQueue(m);
}
+
private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
// Always broadcast the first update
if (lastLoc == null) {
@@ -1857,13 +1291,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
// Check whether sufficient time has passed
- long minTime = record.mMinTime;
- if (loc.getTime() - lastLoc.getTime() < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
+ long minTime = record.mRequest.getFastestInterval();
+ long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
+ if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
return false;
}
// Check whether sufficient distance has been traveled
- double minDistance = record.mMinDistance;
+ double minDistance = record.mRequest.getSmallestDisplacement();
if (minDistance > 0.0) {
if (loc.distanceTo(lastLoc) <= minDistance) {
return false;
@@ -1874,24 +1309,26 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private void handleLocationChangedLocked(Location location, boolean passive) {
+ if (D) Log.d(TAG, "incoming location: " + location);
+
+ long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records == null || records.size() == 0) {
- return;
- }
+ if (records == null || records.size() == 0) return;
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return;
- }
+ if (p == null) return;
+
+ // Add the coarse location as an extra
+ Location coarse = mLocationFudger.getOrCreate(location);
- // Update last known location for provider
- Location lastLocation = mLastKnownLocation.get(provider);
+ // Update last known locations
+ Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
- mLastKnownLocation.put(provider, new Location(location));
- } else {
- lastLocation.set(location);
+ lastLocation = new Location(provider);
+ mLastLocation.put(provider, lastLocation);
}
+ lastLocation.set(location);
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
@@ -1901,18 +1338,25 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
int status = p.getStatus(extras);
ArrayList<Receiver> deadReceivers = null;
-
+ ArrayList<UpdateRecord> deadUpdateRecords = null;
+
// Broadcast location or status to all listeners
- final int N = records.size();
- for (int i=0; i<N; i++) {
- UpdateRecord r = records.get(i);
+ for (UpdateRecord r : records) {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
- if (inBlacklist(receiver.mPackageName)) {
+ if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
+ if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
+ receiver.mPackageName);
continue;
}
+ if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
+ location = lastLocation; // use fine location
+ } else {
+ location = coarse; // use coarse location
+ }
+
Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
if (lastLoc == null) {
@@ -1938,8 +1382,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- // remove receiver if it is dead or we just processed a single shot request
- if (receiverDead || r.mSingleShot) {
+ // track expired records
+ if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
+ if (deadUpdateRecords == null) {
+ deadUpdateRecords = new ArrayList<UpdateRecord>();
+ }
+ deadUpdateRecords.add(r);
+ }
+ // track dead receivers
+ if (receiverDead) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
}
@@ -1948,185 +1399,72 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
}
-
+
+ // remove dead records and receivers outside the loop
if (deadReceivers != null) {
- for (int i=deadReceivers.size()-1; i>=0; i--) {
- removeUpdatesLocked(deadReceivers.get(i));
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
+ }
+ }
+ if (deadUpdateRecords != null) {
+ for (UpdateRecord r : deadUpdateRecords) {
+ r.disposeLocked(true);
}
}
}
private class LocationWorkerHandler extends Handler {
-
@Override
public void handleMessage(Message msg) {
- try {
- if (msg.what == MESSAGE_LOCATION_CHANGED) {
- // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
-
- synchronized (mLock) {
- Location location = (Location) msg.obj;
- String provider = location.getProvider();
- boolean passive = (msg.arg1 == 1);
-
- if (!passive) {
- // notify other providers of the new location
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- if (!provider.equals(p.getName())) {
- p.updateLocation(location);
- }
- }
- }
+ switch (msg.what) {
+ case MSG_LOCATION_CHANGED:
+ handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
+ break;
+ }
+ }
+ }
- if (isAllowedBySettingsLocked(provider)) {
- handleLocationChangedLocked(location, passive);
- }
- }
- } else if (msg.what == MESSAGE_PACKAGE_UPDATED) {
- String packageName = (String) msg.obj;
-
- // reconnect to external providers if there is a better package
- if (mNetworkLocationProviderPackageName != null &&
- mPackageManager.resolveService(
- new Intent(LocationProviderProxy.SERVICE_ACTION)
- .setPackage(packageName), 0) != null) {
- // package implements service, perform full check
- String bestPackage = findBestPackage(
- LocationProviderProxy.SERVICE_ACTION,
- mNetworkLocationProviderPackageName);
- if (packageName.equals(bestPackage)) {
- mNetworkLocationProvider.reconnect(bestPackage);
- mNetworkLocationProviderPackageName = packageName;
- }
- }
- if (mGeocodeProviderPackageName != null &&
- mPackageManager.resolveService(
- new Intent(GeocoderProxy.SERVICE_ACTION)
- .setPackage(packageName), 0) != null) {
- // package implements service, perform full check
- String bestPackage = findBestPackage(
- GeocoderProxy.SERVICE_ACTION,
- mGeocodeProviderPackageName);
- if (packageName.equals(bestPackage)) {
- mGeocodeProvider.reconnect(bestPackage);
- mGeocodeProviderPackageName = packageName;
- }
- }
- }
- } catch (Exception e) {
- // Log, don't crash!
- Slog.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
+ private void handleLocationChanged(Location location, boolean passive) {
+ String provider = location.getProvider();
+
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(location);
+ }
+
+ synchronized (mLock) {
+ if (isAllowedBySettingsLocked(provider)) {
+ handleLocationChangedLocked(location, passive);
}
}
}
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART);
- if (queryRestart
- || action.equals(Intent.ACTION_PACKAGE_REMOVED)
- || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
- || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
- synchronized (mLock) {
- int uidList[] = null;
- if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
- uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
- } else {
- uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)};
- }
- if (uidList == null || uidList.length == 0) {
- return;
- }
- for (int uid : uidList) {
- if (uid >= 0) {
- ArrayList<Receiver> removedRecs = null;
- for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
- for (int j=i.size()-1; j>=0; j--) {
- UpdateRecord ur = i.get(j);
- if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
- if (queryRestart) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
- if (removedRecs == null) {
- removedRecs = new ArrayList<Receiver>();
- }
- if (!removedRecs.contains(ur.mReceiver)) {
- removedRecs.add(ur.mReceiver);
- }
- }
- }
- }
- ArrayList<ProximityAlert> removedAlerts = null;
- for (ProximityAlert i : mProximityAlerts.values()) {
- if (i.mUid == uid) {
- if (queryRestart) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
- if (removedAlerts == null) {
- removedAlerts = new ArrayList<ProximityAlert>();
- }
- if (!removedAlerts.contains(i)) {
- removedAlerts.add(i);
- }
- }
- }
- if (removedRecs != null) {
- for (int i=removedRecs.size()-1; i>=0; i--) {
- removeUpdatesLocked(removedRecs.get(i));
- }
- }
- if (removedAlerts != null) {
- for (int i=removedAlerts.size()-1; i>=0; i--) {
- removeProximityAlertLocked(removedAlerts.get(i).mIntent);
- }
- }
+ public void onPackageDisappeared(String packageName, int reason) {
+ // remove all receivers associated with this package name
+ synchronized (mLock) {
+ ArrayList<Receiver> deadReceivers = null;
+
+ for (Receiver receiver : mReceivers.values()) {
+ if (receiver.mPackageName.equals(packageName)) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<Receiver>();
}
+ deadReceivers.add(receiver);
}
}
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- boolean noConnectivity =
- intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- if (!noConnectivity) {
- mNetworkState = LocationProvider.AVAILABLE;
- } else {
- mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
- }
- final ConnectivityManager connManager = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- final NetworkInfo info = connManager.getActiveNetworkInfo();
-
- // Notify location providers of current network state
- synchronized (mLock) {
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface provider = mProviders.get(i);
- if (provider.requiresNetwork()) {
- provider.updateNetworkState(mNetworkState, info);
- }
+ // perform removal outside of mReceivers loop
+ if (deadReceivers != null) {
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
}
}
}
}
};
- private final PackageMonitor mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- // Called by main thread; divert work to LocationWorker.
- Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
- }
- @Override
- public void onPackageAdded(String packageName, int uid) {
- // Called by main thread; divert work to LocationWorker.
- Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
- }
- };
-
// Wake locks
private void incrementPendingBroadcasts() {
@@ -2166,10 +1504,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Geocoder
+ @Override
public boolean geocoderIsPresent() {
return mGeocodeProvider != null;
}
+ @Override
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
if (mGeocodeProvider != null) {
@@ -2180,6 +1520,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
+ @Override
public String getFromLocationName(String locationName,
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
@@ -2205,12 +1546,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
- }
+ }
}
- public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ @Override
+ public void addTestProvider(String name, ProviderProperties properties) {
checkMockPermissionsSafe();
if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
@@ -2219,30 +1559,28 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
long identity = Binder.clearCallingIdentity();
synchronized (mLock) {
- MockProvider provider = new MockProvider(name, this,
- requiresNetwork, requiresSatellite,
- requiresCell, hasMonetaryCost, supportsAltitude,
- supportsSpeed, supportsBearing, powerRequirement, accuracy);
+ MockProvider provider = new MockProvider(name, this, properties);
// remove the real provider if we are replacing GPS or network provider
if (LocationManager.GPS_PROVIDER.equals(name)
- || LocationManager.NETWORK_PROVIDER.equals(name)) {
+ || LocationManager.NETWORK_PROVIDER.equals(name)
+ || LocationManager.FUSED_PROVIDER.equals(name)) {
LocationProviderInterface p = mProvidersByName.get(name);
if (p != null) {
- p.enableLocationTracking(false);
- removeProvider(p);
+ removeProviderLocked(p);
}
}
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
- addProvider(provider);
+ addProviderLocked(provider);
mMockProviders.put(name, provider);
- mLastKnownLocation.put(name, null);
+ mLastLocation.put(name, null);
updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
}
+ @Override
public void removeTestProvider(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2251,22 +1589,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
long identity = Binder.clearCallingIdentity();
- removeProvider(mProvidersByName.get(provider));
+ removeProviderLocked(mProvidersByName.get(provider));
mMockProviders.remove(mockProvider);
- // reinstall real provider if we were mocking GPS or network provider
- if (LocationManager.GPS_PROVIDER.equals(provider) &&
- mGpsLocationProvider != null) {
- addProvider(mGpsLocationProvider);
- } else if (LocationManager.NETWORK_PROVIDER.equals(provider) &&
- mNetworkLocationProvider != null) {
- addProvider(mNetworkLocationProvider);
- }
- mLastKnownLocation.put(provider, null);
+
+ // reinstate real provider if available
+ LocationProviderInterface realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
updateProvidersLocked();
Binder.restoreCallingIdentity(identity);
}
}
+ @Override
public void setTestProviderLocation(String provider, Location loc) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2281,6 +1618,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderLocation(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2292,6 +1630,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void setTestProviderEnabled(String provider, boolean enabled) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2314,6 +1653,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderEnabled(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2329,6 +1669,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2340,6 +1681,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderStatus(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2351,119 +1693,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- public class BlacklistObserver extends ContentObserver {
- public BlacklistObserver(Handler handler) {
- super(handler);
- }
- @Override
- public void onChange(boolean selfChange) {
- reloadBlacklist();
- }
- }
-
- private void loadBlacklist() {
- // Register for changes
- BlacklistObserver observer = new BlacklistObserver(mLocationHandler);
- mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
- BLACKLIST_CONFIG_NAME), false, observer);
- mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
- WHITELIST_CONFIG_NAME), false, observer);
- reloadBlacklist();
- }
-
- private void reloadBlacklist() {
- String blacklist[] = getStringArray(BLACKLIST_CONFIG_NAME);
- String whitelist[] = getStringArray(WHITELIST_CONFIG_NAME);
- synchronized (mLock) {
- mWhitelist = whitelist;
- Slog.i(TAG, "whitelist: " + arrayToString(mWhitelist));
- mBlacklist = blacklist;
- Slog.i(TAG, "blacklist: " + arrayToString(mBlacklist));
- }
- }
-
- private static String arrayToString(String[] array) {
- StringBuilder s = new StringBuilder();
- s.append('[');
- boolean first = true;
- for (String a : array) {
- if (!first) s.append(',');
- first = false;
- s.append(a);
- }
- s.append(']');
- return s.toString();
- }
-
- private String[] getStringArray(String key) {
- String flatString = Settings.Secure.getString(mContext.getContentResolver(), key);
- if (flatString == null) {
- return new String[0];
- }
- String[] splitStrings = flatString.split(",");
- ArrayList<String> result = new ArrayList<String>();
- for (String pkg : splitStrings) {
- pkg = pkg.trim();
- if (pkg.isEmpty()) {
- continue;
- }
- result.add(pkg);
- }
- return result.toArray(new String[result.size()]);
- }
-
- /**
- * Return true if in blacklist, and not in whitelist.
- */
- private boolean inBlacklist(String packageName) {
- synchronized (mLock) {
- for (String black : mBlacklist) {
- if (packageName.startsWith(black)) {
- if (inWhitelist(packageName)) {
- continue;
- } else {
- if (LOCAL_LOGV) Log.d(TAG, "dropping location (blacklisted): "
- + packageName + " matches " + black);
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Return true if any of packages are in whitelist
- */
- private boolean inWhitelist(String pkg) {
- synchronized (mLock) {
- for (String white : mWhitelist) {
- if (pkg.startsWith(white)) return true;
- }
- }
- return false;
- }
-
- private void checkPackageName(int uid, String packageName) {
- if (packageName == null) {
- throw new SecurityException("packageName cannot be null");
- }
- String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages == null) {
- throw new SecurityException("invalid UID " + uid);
- }
- for (String pkg : packages) {
- if (packageName.equals(pkg)) return;
- }
- throw new SecurityException("invalid package name");
- }
-
private void log(String log) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, log);
}
}
-
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2472,83 +1708,65 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
+ ", uid=" + Binder.getCallingUid());
return;
}
-
+
synchronized (mLock) {
pw.println("Current Location Manager state:");
- pw.println(" sProvidersLoaded=" + sProvidersLoaded);
- pw.println(" Listeners:");
- int N = mReceivers.size();
- for (int i=0; i<N; i++) {
- pw.println(" " + mReceivers.get(i));
- }
pw.println(" Location Listeners:");
- for (Receiver i : mReceivers.values()) {
- pw.println(" " + i + ":");
- for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
- pw.println(" " + j.getKey() + ":");
- j.getValue().dump(pw, " ");
- }
+ for (Receiver receiver : mReceivers.values()) {
+ pw.println(" " + receiver);
}
- pw.println(" Package blacklist:" + arrayToString(mBlacklist));
- pw.println(" Package whitelist:" + arrayToString(mWhitelist));
pw.println(" Records by Provider:");
- for (Map.Entry<String, ArrayList<UpdateRecord>> i
- : mRecordsByProvider.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- for (UpdateRecord j : i.getValue()) {
- pw.println(" " + j + ":");
- j.dump(pw, " ");
+ for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
+ pw.println(" " + entry.getKey() + ":");
+ for (UpdateRecord record : entry.getValue()) {
+ pw.println(" " + record);
}
}
pw.println(" Last Known Locations:");
- for (Map.Entry<String, Location> i
- : mLastKnownLocation.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- i.getValue().dump(new PrintWriterPrinter(pw), " ");
- }
- if (mProximityAlerts.size() > 0) {
- pw.println(" Proximity Alerts:");
- for (Map.Entry<PendingIntent, ProximityAlert> i
- : mProximityAlerts.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- i.getValue().dump(pw, " ");
- }
- }
- if (mProximitiesEntered.size() > 0) {
- pw.println(" Proximities Entered:");
- for (ProximityAlert i : mProximitiesEntered) {
- pw.println(" " + i + ":");
- i.dump(pw, " ");
- }
+ for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
+ String provider = entry.getKey();
+ Location location = entry.getValue();
+ pw.println(" " + provider + ": " + location);
}
- pw.println(" mProximityReceiver=" + mProximityReceiver);
- pw.println(" mProximityListener=" + mProximityListener);
+
+ mGeofenceManager.dump(pw);
+
if (mEnabledProviders.size() > 0) {
pw.println(" Enabled Providers:");
for (String i : mEnabledProviders) {
pw.println(" " + i);
}
-
+
}
if (mDisabledProviders.size() > 0) {
pw.println(" Disabled Providers:");
for (String i : mDisabledProviders) {
pw.println(" " + i);
}
-
}
+ pw.append(" ");
+ mBlacklist.dump(pw);
if (mMockProviders.size() > 0) {
pw.println(" Mock Providers:");
for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
i.getValue().dump(pw, " ");
}
}
+
+ pw.append(" fudger: ");
+ mLocationFudger.dump(fd, pw, args);
+
+ if (args.length > 0 && "short".equals(args[0])) {
+ return;
+ }
for (LocationProviderInterface provider: mProviders) {
- String state = provider.getInternalState();
- if (state != null) {
- pw.println(provider.getName() + " Internal State:");
- pw.write(state);
+ pw.print(provider.getName() + " Internal State");
+ if (provider instanceof LocationProviderProxy) {
+ LocationProviderProxy proxy = (LocationProviderProxy) provider;
+ pw.print(" (" + proxy.getConnectedPackageName() + ")");
}
+ pw.println(":");
+ provider.dump(fd, pw, args);
}
}
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 04267a3..bb5d552 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -48,7 +48,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.storage.IMountService;
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
@@ -1713,7 +1713,7 @@ class MountService extends IMountService.Stub
return false;
}
- final int packageUid = mPms.getPackageUid(packageName, UserId.getUserId(callerUid));
+ final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
if (DEBUG_OBB) {
Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index f71125a..92af9a9 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -35,6 +35,9 @@ import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
import java.util.LinkedList;
/**
@@ -482,102 +485,108 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
private static class ResponseQueue {
- private static class Response {
+ private static class PendingCmd {
public int cmdNum;
- public LinkedList<NativeDaemonEvent> responses = new LinkedList<NativeDaemonEvent>();
+ public BlockingQueue<NativeDaemonEvent> responses =
+ new ArrayBlockingQueue<NativeDaemonEvent>(10);
public String request;
- public Response(int c, String r) {cmdNum = c; request = r;}
+
+ // The availableResponseCount member is used to track when we can remove this
+ // instance from the ResponseQueue.
+ // This is used under the protection of a sync of the mPendingCmds object.
+ // A positive value means we've had more writers retreive this object while
+ // a negative value means we've had more readers. When we've had an equal number
+ // (it goes to zero) we can remove this object from the mPendingCmds list.
+ // Note that we may have more responses for this command (and more readers
+ // coming), but that would result in a new PendingCmd instance being created
+ // and added with the same cmdNum.
+ // Also note that when this goes to zero it just means a parity of readers and
+ // writers have retrieved this object - not that they are done using it. The
+ // responses queue may well have more responses yet to be read or may get more
+ // responses added to it. But all those readers/writers have retreived and
+ // hold references to this instance already so it can be removed from
+ // mPendingCmds queue.
+ public int availableResponseCount;
+ public PendingCmd(int c, String r) {cmdNum = c; request = r;}
}
- private final LinkedList<Response> mResponses;
+ private final LinkedList<PendingCmd> mPendingCmds;
private int mMaxCount;
ResponseQueue(int maxCount) {
- mResponses = new LinkedList<Response>();
+ mPendingCmds = new LinkedList<PendingCmd>();
mMaxCount = maxCount;
}
public void add(int cmdNum, NativeDaemonEvent response) {
- Response found = null;
- synchronized (mResponses) {
- for (Response r : mResponses) {
- if (r.cmdNum == cmdNum) {
- found = r;
+ PendingCmd found = null;
+ synchronized (mPendingCmds) {
+ for (PendingCmd pendingCmd : mPendingCmds) {
+ if (pendingCmd.cmdNum == cmdNum) {
+ found = pendingCmd;
break;
}
}
if (found == null) {
// didn't find it - make sure our queue isn't too big before adding
- // another..
- while (mResponses.size() >= mMaxCount) {
+ while (mPendingCmds.size() >= mMaxCount) {
Slog.e("NativeDaemonConnector.ResponseQueue",
- "more buffered than allowed: " + mResponses.size() +
+ "more buffered than allowed: " + mPendingCmds.size() +
" >= " + mMaxCount);
// let any waiter timeout waiting for this
- Response r = mResponses.remove();
+ PendingCmd pendingCmd = mPendingCmds.remove();
Slog.e("NativeDaemonConnector.ResponseQueue",
- "Removing request: " + r.request + " (" + r.cmdNum + ")");
+ "Removing request: " + pendingCmd.request + " (" +
+ pendingCmd.cmdNum + ")");
}
- found = new Response(cmdNum, null);
- mResponses.add(found);
+ found = new PendingCmd(cmdNum, null);
+ mPendingCmds.add(found);
}
- found.responses.add(response);
- }
- synchronized (found) {
- found.notify();
+ found.availableResponseCount++;
+ // if a matching remove call has already retrieved this we can remove this
+ // instance from our list
+ if (found.availableResponseCount == 0) mPendingCmds.remove(found);
}
+ try {
+ found.responses.put(response);
+ } catch (InterruptedException e) { }
}
// note that the timeout does not count time in deep sleep. If you don't want
// the device to sleep, hold a wakelock
public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) {
- long endTime = SystemClock.uptimeMillis() + timeoutMs;
- long nowTime;
- Response found = null;
- while (true) {
- synchronized (mResponses) {
- for (Response response : mResponses) {
- if (response.cmdNum == cmdNum) {
- found = response;
- // how many response fragments are left
- switch (response.responses.size()) {
- case 0: // haven't got any - must wait
- break;
- case 1: // last one - remove this from the master list
- mResponses.remove(response); // fall through
- default: // take one and move on
- response.request = origCmd;
- return response.responses.remove();
- }
- }
- }
- nowTime = SystemClock.uptimeMillis();
- if (endTime <= nowTime) {
- Slog.e("NativeDaemonConnector.ResponseQueue",
- "Timeout waiting for response");
- return null;
- }
- /* pre-allocate so we have something unique to wait on */
- if (found == null) {
- found = new Response(cmdNum, origCmd);
- mResponses.add(found);
+ PendingCmd found = null;
+ synchronized (mPendingCmds) {
+ for (PendingCmd pendingCmd : mPendingCmds) {
+ if (pendingCmd.cmdNum == cmdNum) {
+ found = pendingCmd;
+ break;
}
}
- try {
- synchronized (found) {
- found.wait(endTime - nowTime);
- }
- } catch (InterruptedException e) {
- // loop around to check if we're done or if it's time to stop waiting
+ if (found == null) {
+ found = new PendingCmd(cmdNum, origCmd);
+ mPendingCmds.add(found);
}
+ found.availableResponseCount--;
+ // if a matching add call has already retrieved this we can remove this
+ // instance from our list
+ if (found.availableResponseCount == 0) mPendingCmds.remove(found);
+ }
+ NativeDaemonEvent result = null;
+ try {
+ result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {}
+ if (result == null) {
+ Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response");
}
+ return result;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Pending requests:");
- synchronized (mResponses) {
- for (Response response : mResponses) {
- pw.println(" Cmd " + response.cmdNum + " - " + response.request);
+ synchronized (mPendingCmds) {
+ for (PendingCmd pendingCmd : mPendingCmds) {
+ pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.request);
}
}
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 11644e3..efa16af 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -35,6 +35,7 @@ import static com.android.server.NetworkManagementService.NetdResponseCode.Tethe
import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
+import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.Context;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
@@ -55,7 +56,9 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
+import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Maps;
import java.io.BufferedReader;
@@ -91,6 +94,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private static final String ADD = "add";
private static final String REMOVE = "remove";
+ private static final String ALLOW = "allow";
+ private static final String DENY = "deny";
+
private static final String DEFAULT = "default";
private static final String SECONDARY = "secondary";
@@ -121,6 +127,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public static final int InterfaceChange = 600;
public static final int BandwidthControl = 601;
+ public static final int InterfaceClassActivity = 613;
}
/**
@@ -151,7 +158,23 @@ public class NetworkManagementService extends INetworkManagementService.Stub
/** Set of UIDs with active reject rules. */
private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+ private Object mIdleTimerLock = new Object();
+ /** Set of interfaces with active idle timers. */
+ private static class IdleTimerParams {
+ public final int timeout;
+ public final String label;
+ public int networkCount;
+
+ IdleTimerParams(int timeout, String label) {
+ this.timeout = timeout;
+ this.label = label;
+ this.networkCount = 1;
+ }
+ }
+ private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
+
private volatile boolean mBandwidthControlEnabled;
+ private volatile boolean mFirewallEnabled;
/**
* Constructs a new NetworkManagementService instance
@@ -278,6 +301,20 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
/**
+ * Notify our observers of a change in the data activity state of the interface
+ */
+ private void notifyInterfaceClassActivity(String label, boolean active) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
+ } catch (RemoteException e) {
+ }
+ }
+ mObservers.finishBroadcast();
+ }
+
+ /**
* Prepare native daemon once connected, enabling modules and pushing any
* existing in-memory rules.
*/
@@ -332,6 +369,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
}
+
+ // TODO: Push any existing firewall state
+ setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
}
//
@@ -403,6 +443,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub
throw new IllegalStateException(
String.format("Invalid event from daemon (%s)", raw));
// break;
+ case NetdResponseCode.InterfaceClassActivity:
+ /*
+ * An network interface class state changed (active/idle)
+ * Format: "NNN IfaceClass <active/idle> <label>"
+ */
+ if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) {
+ throw new IllegalStateException(
+ String.format("Invalid event from daemon (%s)", raw));
+ }
+ boolean isActive = cooked[2].equals("active");
+ notifyInterfaceClassActivity(cooked[3], isActive);
+ return true;
+ // break;
default: break;
}
return false;
@@ -780,6 +833,36 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return event.getMessage().endsWith("started");
}
+ // TODO(BT) Remove
+ public void startReverseTethering(String iface)
+ throws IllegalStateException {
+ if (DBG) Slog.d(TAG, "startReverseTethering in");
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ // cmd is "tether start first_start first_stop second_start second_stop ..."
+ // an odd number of addrs will fail
+ String cmd = "tether start-reverse";
+ cmd += " " + iface;
+ if (DBG) Slog.d(TAG, "startReverseTethering cmd: " + cmd);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon");
+ }
+ BluetoothTetheringDataTracker.getInstance().startReverseTether(iface);
+
+ }
+
+ // TODO(BT) Remove
+ public void stopReverseTethering() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.doCommand("tether stop-reverse");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
+ }
+ BluetoothTetheringDataTracker.getInstance().stopReverseTether();
+ }
+
@Override
public void tetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -923,14 +1006,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub
@Override
public void startAccessPoint(
- WifiConfiguration wifiConfig, String wlanIface, String softapIface) {
+ WifiConfiguration wifiConfig, String wlanIface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
wifiFirmwareReload(wlanIface, "AP");
if (wifiConfig == null) {
- mConnector.execute("softap", "set", wlanIface, softapIface);
+ mConnector.execute("softap", "set", wlanIface);
} else {
- mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
+ mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
getSecurityType(wifiConfig), wifiConfig.preSharedKey);
}
mConnector.execute("softap", "startap");
@@ -966,7 +1049,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
mConnector.execute("softap", "stopap");
- mConnector.execute("softap", "stop", wlanIface);
wifiFirmwareReload(wlanIface, "STA");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
@@ -974,13 +1056,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
- public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) {
+ public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
if (wifiConfig == null) {
- mConnector.execute("softap", "set", wlanIface, softapIface);
+ mConnector.execute("softap", "set", wlanIface);
} else {
- mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
+ mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
getSecurityType(wifiConfig), wifiConfig.preSharedKey);
}
} catch (NativeDaemonConnectorException e) {
@@ -989,6 +1071,51 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
+ public void addIdleTimer(String iface, int timeout, String label) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ if (DBG) Slog.d(TAG, "Adding idletimer");
+
+ synchronized (mIdleTimerLock) {
+ IdleTimerParams params = mActiveIdleTimers.get(iface);
+ if (params != null) {
+ // the interface already has idletimer, update network count
+ params.networkCount++;
+ return;
+ }
+
+ try {
+ mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), label);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, label));
+ }
+ }
+
+ @Override
+ public void removeIdleTimer(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ if (DBG) Slog.d(TAG, "Removing idletimer");
+
+ synchronized (mIdleTimerLock) {
+ IdleTimerParams params = mActiveIdleTimers.get(iface);
+ if (params == null || --(params.networkCount) > 0) {
+ return;
+ }
+
+ try {
+ mConnector.execute("idletimer", "remove", iface,
+ Integer.toString(params.timeout), params.label);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ mActiveIdleTimers.remove(iface);
+ }
+ }
+
+ @Override
public NetworkStats getNetworkStatsSummaryDev() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return mStatsFactory.readNetworkStatsSummaryDev();
@@ -1307,7 +1434,72 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
- /** {@inheritDoc} */
+ @Override
+ public void setFirewallEnabled(boolean enabled) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("firewall", enabled ? "enable" : "disable");
+ mFirewallEnabled = enabled;
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public boolean isFirewallEnabled() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ return mFirewallEnabled;
+ }
+
+ @Override
+ public void setFirewallInterfaceRule(String iface, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_interface_rule", iface, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setFirewallEgressSourceRule(String addr, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setFirewallUidRule(int uid, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_uid_rule", uid, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
public void monitor() {
if (mConnector != null) {
mConnector.monitor();
@@ -1338,5 +1530,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
pw.println("]");
}
+
+ pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index f6d3b608..d6fed39 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -48,11 +48,13 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
+import android.service.dreams.IDreamManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -61,7 +63,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import com.android.internal.os.AtomicFile;
import com.android.internal.statusbar.StatusBarNotification;
import com.android.internal.util.FastXmlSerializer;
@@ -148,6 +149,8 @@ public class NotificationManagerService extends INotificationManager.Stub
private AtomicFile mPolicyFile;
private HashSet<String> mBlockedPackages = new HashSet<String>();
+ private IDreamManager mSandman;
+
private static final int DB_VERSION = 1;
private static final String TAG_BODY = "notification-policy";
@@ -634,6 +637,8 @@ public class NotificationManagerService extends INotificationManager.Stub
void systemReady() {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
+ mSandman = IDreamManager.Stub.asInterface(
+ ServiceManager.getService("dreams"));
// no beeping until we're basically done booting
mSystemReady = true;
@@ -972,6 +977,16 @@ public class NotificationManagerService extends INotificationManager.Stub
| Notification.FLAG_NO_CLEAR;
}
+ // Stop screensaver if the notification has a full-screen intent.
+ // (like an incoming phone call)
+ if (notification.fullScreenIntent != null && mSandman != null) {
+ try {
+ mSandman.awaken();
+ } catch (RemoteException e) {
+ // noop
+ }
+ }
+
if (notification.icon != 0) {
StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
r.uid, r.initialPid, score, notification);
@@ -1257,7 +1272,7 @@ public class NotificationManagerService extends INotificationManager.Stub
void checkCallerIsSystem() {
int uid = Binder.getCallingUid();
- if (uid == Process.SYSTEM_UID || uid == 0) {
+ if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
return;
}
throw new SecurityException("Disallowed call for uid " + uid);
@@ -1265,13 +1280,13 @@ public class NotificationManagerService extends INotificationManager.Stub
void checkCallerIsSystemOrSameApp(String pkg) {
int uid = Binder.getCallingUid();
- if (uid == Process.SYSTEM_UID || uid == 0) {
+ if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
return;
}
try {
ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
pkg, 0);
- if (!UserId.isSameApp(ai.uid, uid)) {
+ if (!UserHandle.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
deleted file mode 100644
index 888ec69..0000000
--- a/services/java/com/android/server/PowerManagerService.java
+++ /dev/null
@@ -1,3405 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.server.am.BatteryStatsService;
-import com.android.server.pm.ShutdownThread;
-
-import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.ContentQueryMap;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.hardware.SystemSensorManager;
-import android.os.BatteryManager;
-import android.os.BatteryStats;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.IPowerManager;
-import android.os.LocalPowerManager;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-import android.view.WindowManagerPolicy;
-import static android.view.WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR;
-import static android.provider.Settings.System.DIM_SCREEN;
-import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
-import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE;
-import static android.provider.Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ;
-import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
-import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN;
-import static android.provider.Settings.System.WINDOW_ANIMATION_SCALE;
-import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Observable;
-import java.util.Observer;
-
-public class PowerManagerService extends IPowerManager.Stub
- implements LocalPowerManager, Watchdog.Monitor {
- private static final int NOMINAL_FRAME_TIME_MS = 1000/60;
-
- private static final String TAG = "PowerManagerService";
- static final String PARTIAL_NAME = "PowerManagerService";
-
- // Wake lock that ensures that the CPU is running. The screen might not be on.
- private static final int PARTIAL_WAKE_LOCK_ID = 1;
-
- // Wake lock that ensures that the screen is on.
- private static final int FULL_WAKE_LOCK_ID = 2;
-
- static final boolean DEBUG_SCREEN_ON = false;
-
- private static final boolean LOG_PARTIAL_WL = false;
-
- // Indicates whether touch-down cycles should be logged as part of the
- // LOG_POWER_SCREEN_STATE log events
- private static final boolean LOG_TOUCH_DOWNS = true;
-
- private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
- | PowerManager.SCREEN_DIM_WAKE_LOCK
- | PowerManager.SCREEN_BRIGHT_WAKE_LOCK
- | PowerManager.FULL_WAKE_LOCK
- | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
-
- // time since last state: time since last event:
- // The short keylight delay comes from secure settings; this is the default.
- private static final int SHORT_KEYLIGHT_DELAY_DEFAULT = 6000; // t+6 sec
- private static final int MEDIUM_KEYLIGHT_DELAY = 15000; // t+15 sec
- private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec
- private static final int LONG_DIM_TIME = 7000; // t+N-5 sec
-
- // How long to wait to debounce light sensor changes in milliseconds
- private static final int LIGHT_SENSOR_DELAY = 2000;
-
- // light sensor events rate in microseconds
- private static final int LIGHT_SENSOR_RATE = 1000000;
-
- // Expansion of range of light values when applying scale from light
- // sensor brightness setting, in the [0..255] brightness range.
- private static final int LIGHT_SENSOR_RANGE_EXPANSION = 20;
-
- // Scaling factor of the light sensor brightness setting when applying
- // it to the final brightness.
- private static final int LIGHT_SENSOR_OFFSET_SCALE = 8;
-
- // For debouncing the proximity sensor in milliseconds
- private static final int PROXIMITY_SENSOR_DELAY = 1000;
-
- // trigger proximity if distance is less than 5 cm
- private static final float PROXIMITY_THRESHOLD = 5.0f;
-
- // Cached secure settings; see updateSettingsValues()
- private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
-
- // Default timeout for screen off, if not found in settings database = 15 seconds.
- private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;
-
- // Screen brightness should always have a value, but just in case...
- private static final int DEFAULT_SCREEN_BRIGHTNESS = 192;
-
- // Threshold for BRIGHTNESS_LOW_BATTERY (percentage)
- // Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD
- private static final int LOW_BATTERY_THRESHOLD = 10;
-
- // flags for setPowerState
- private static final int ALL_LIGHTS_OFF = 0x00000000;
- private static final int SCREEN_ON_BIT = 0x00000001;
- private static final int SCREEN_BRIGHT_BIT = 0x00000002;
- private static final int BUTTON_BRIGHT_BIT = 0x00000004;
- private static final int KEYBOARD_BRIGHT_BIT = 0x00000008;
- private static final int BATTERY_LOW_BIT = 0x00000010;
-
- // values for setPowerState
-
- // SCREEN_OFF == everything off
- private static final int SCREEN_OFF = 0x00000000;
-
- // SCREEN_DIM == screen on, screen backlight dim
- private static final int SCREEN_DIM = SCREEN_ON_BIT;
-
- // SCREEN_BRIGHT == screen on, screen backlight bright
- private static final int SCREEN_BRIGHT = SCREEN_ON_BIT | SCREEN_BRIGHT_BIT;
-
- // SCREEN_BUTTON_BRIGHT == screen on, screen and button backlights bright
- private static final int SCREEN_BUTTON_BRIGHT = SCREEN_BRIGHT | BUTTON_BRIGHT_BIT;
-
- // SCREEN_BUTTON_BRIGHT == screen on, screen, button and keyboard backlights bright
- private static final int ALL_BRIGHT = SCREEN_BUTTON_BRIGHT | KEYBOARD_BRIGHT_BIT;
-
- // used for noChangeLights in setPowerState()
- private static final int LIGHTS_MASK = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT;
-
- // animate screen lights in PowerManager (as opposed to SurfaceFlinger)
- boolean mAnimateScreenLights = true;
-
- static final int ANIM_STEPS = 60; // nominal # of frames at 60Hz
- // Slower animation for autobrightness changes
- static final int AUTOBRIGHTNESS_ANIM_STEPS = 2 * ANIM_STEPS;
- // Even slower animation for autodimness changes. Set to max to effectively disable dimming.
- // Note 100 is used to keep the mWindowScaleAnimation scaling below from overflowing an int.
- static final int AUTODIMNESS_ANIM_STEPS = Integer.MAX_VALUE / (NOMINAL_FRAME_TIME_MS * 100);
- // Number of steps when performing a more immediate brightness change.
- static final int IMMEDIATE_ANIM_STEPS = 4;
-
- // These magic numbers are the initial state of the LEDs at boot. Ideally
- // we should read them from the driver, but our current hardware returns 0
- // for the initial value. Oops!
- static final int INITIAL_SCREEN_BRIGHTNESS = 255;
- static final int INITIAL_BUTTON_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF;
- static final int INITIAL_KEYBOARD_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF;
-
- private final int MY_UID;
- private final int MY_PID;
-
- private boolean mDoneBooting = false;
- private boolean mBootCompleted = false;
- private boolean mHeadless = false;
- private int mStayOnConditions = 0;
- private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
- private final int[] mBroadcastWhy = new int[3];
- private boolean mPreparingForScreenOn = false;
- private boolean mSkippedScreenOn = false;
- private boolean mInitialized = false;
- private int mPartialCount = 0;
- private int mPowerState;
- // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
- // WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT or WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
- private int mScreenOffReason;
- private int mUserState;
- private boolean mKeyboardVisible = false;
- private boolean mUserActivityAllowed = true;
- private int mProximityWakeLockCount = 0;
- private boolean mProximitySensorEnabled = false;
- private boolean mProximitySensorActive = false;
- private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active
- private long mLastProximityEventTime;
- private int mScreenOffTimeoutSetting;
- private int mMaximumScreenOffTimeout = Integer.MAX_VALUE;
- private int mKeylightDelay;
- private int mDimDelay;
- private int mScreenOffDelay;
- private int mWakeLockState;
- private long mLastEventTime = 0;
- private long mScreenOffTime;
- private volatile WindowManagerPolicy mPolicy;
- private final LockList mLocks = new LockList();
- private Intent mScreenOffIntent;
- private Intent mScreenOnIntent;
- private LightsService mLightsService;
- private Context mContext;
- private LightsService.Light mLcdLight;
- private LightsService.Light mButtonLight;
- private LightsService.Light mKeyboardLight;
- private LightsService.Light mAttentionLight;
- private UnsynchronizedWakeLock mBroadcastWakeLock;
- private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock;
- private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock;
- private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
- private UnsynchronizedWakeLock mProximityPartialLock;
- private HandlerThread mHandlerThread;
- private Handler mScreenOffHandler;
- private Handler mScreenBrightnessHandler;
- private Handler mHandler;
- private final TimeoutTask mTimeoutTask = new TimeoutTask();
- private ScreenBrightnessAnimator mScreenBrightnessAnimator;
- private boolean mWaitingForFirstLightSensor = false;
- private boolean mStillNeedSleepNotification;
- private boolean mIsPowered = false;
- private IActivityManager mActivityService;
- private IBatteryStats mBatteryStats;
- private BatteryService mBatteryService;
- private SensorManager mSensorManager;
- private Sensor mProximitySensor;
- private Sensor mLightSensor;
- private boolean mLightSensorEnabled;
- private float mLightSensorValue = -1;
- private boolean mProxIgnoredBecauseScreenTurnedOff = false;
- private int mHighestLightSensorValue = -1;
- private boolean mLightSensorPendingDecrease = false;
- private boolean mLightSensorPendingIncrease = false;
- private float mLightSensorPendingValue = -1;
- private float mLightSensorAdjustSetting = 0;
- private int mLightSensorScreenBrightness = -1;
- private int mLightSensorButtonBrightness = -1;
- private int mLightSensorKeyboardBrightness = -1;
- private boolean mDimScreen = true;
- private boolean mIsDocked = false;
- private long mNextTimeout;
- private volatile int mPokey = 0;
- private volatile boolean mPokeAwakeOnSet = false;
- private volatile boolean mInitComplete = false;
- private final HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>();
- // mLastScreenOnTime is the time the screen was last turned on
- private long mLastScreenOnTime;
- private boolean mPreventScreenOn;
- private int mScreenBrightnessSetting = DEFAULT_SCREEN_BRIGHTNESS;
- private int mScreenBrightnessOverride = -1;
- private int mButtonBrightnessOverride = -1;
- private int mScreenBrightnessDim;
- private boolean mUseSoftwareAutoBrightness;
- private boolean mAutoBrightessEnabled;
- private int[] mAutoBrightnessLevels;
- private int[] mLcdBacklightValues;
- private int[] mButtonBacklightValues;
- private int[] mKeyboardBacklightValues;
- private int mLightSensorWarmupTime;
- boolean mUnplugTurnsOnScreen;
- private int mWarningSpewThrottleCount;
- private long mWarningSpewThrottleTime;
- private int mAnimationSetting = ANIM_SETTING_OFF;
- private float mWindowScaleAnimation;
-
- // Must match with the ISurfaceComposer constants in C++.
- private static final int ANIM_SETTING_ON = 0x01;
- private static final int ANIM_SETTING_OFF = 0x10;
-
- // Used when logging number and duration of touch-down cycles
- private long mTotalTouchDownTime;
- private long mLastTouchDown;
- private int mTouchCycles;
-
- // could be either static or controllable at runtime
- private static final boolean mSpew = false;
- private static final boolean mDebugProximitySensor = (false || mSpew);
- private static final boolean mDebugLightSensor = (false || mSpew);
- private static final boolean mDebugLightAnimation = (false || mSpew);
-
- private native void nativeInit();
- private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
- private native void nativeStartSurfaceFlingerAnimation(int mode);
- private static native void nativeAcquireWakeLock(int lock, String id);
- private static native void nativeReleaseWakeLock(String id);
- private static native int nativeSetScreenState(boolean on);
- private static native void nativeShutdown();
- private static native void nativeReboot(String reason) throws IOException;
-
- /*
- static PrintStream mLog;
- static {
- try {
- mLog = new PrintStream("/data/power.log");
- }
- catch (FileNotFoundException e) {
- android.util.Slog.e(TAG, "Life is hard", e);
- }
- }
- static class Log {
- static void d(String tag, String s) {
- mLog.println(s);
- android.util.Slog.d(tag, s);
- }
- static void i(String tag, String s) {
- mLog.println(s);
- android.util.Slog.i(tag, s);
- }
- static void w(String tag, String s) {
- mLog.println(s);
- android.util.Slog.w(tag, s);
- }
- static void e(String tag, String s) {
- mLog.println(s);
- android.util.Slog.e(tag, s);
- }
- }
- */
-
- /**
- * This class works around a deadlock between the lock in PowerManager.WakeLock
- * and our synchronizing on mLocks. PowerManager.WakeLock synchronizes on its
- * mToken object so it can be accessed from any thread, but it calls into here
- * with its lock held. This class is essentially a reimplementation of
- * PowerManager.WakeLock, but without that extra synchronized block, because we'll
- * only call it with our own locks held.
- */
- private class UnsynchronizedWakeLock {
- int mFlags;
- String mTag;
- IBinder mToken;
- int mCount = 0;
- boolean mRefCounted;
- boolean mHeld;
-
- UnsynchronizedWakeLock(int flags, String tag, boolean refCounted) {
- mFlags = flags;
- mTag = tag;
- mToken = new Binder();
- mRefCounted = refCounted;
- }
-
- public void acquire() {
- if (!mRefCounted || mCount++ == 0) {
- long ident = Binder.clearCallingIdentity();
- try {
- PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, MY_PID, mTag, null);
- mHeld = true;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- public void release() {
- if (!mRefCounted || --mCount == 0) {
- PowerManagerService.this.releaseWakeLockLocked(mToken, 0, false);
- mHeld = false;
- }
- if (mCount < 0) {
- throw new RuntimeException("WakeLock under-locked " + mTag);
- }
- }
-
- public boolean isHeld()
- {
- return mHeld;
- }
-
- public String toString() {
- return "UnsynchronizedWakeLock(mFlags=0x" + Integer.toHexString(mFlags)
- + " mCount=" + mCount + " mHeld=" + mHeld + ")";
- }
- }
-
- private final class BatteryReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mLocks) {
- boolean wasPowered = mIsPowered;
- mIsPowered = mBatteryService.isPowered();
-
- if (mIsPowered != wasPowered) {
- // update mStayOnWhilePluggedIn wake lock
- updateWakeLockLocked();
-
- // treat plugging and unplugging the devices as a user activity.
- // users find it disconcerting when they unplug the device
- // and it shuts off right away.
- // to avoid turning on the screen when unplugging, we only trigger
- // user activity when screen was already on.
- // temporarily set mUserActivityAllowed to true so this will work
- // even when the keyguard is on.
- // However, you can also set config_unplugTurnsOnScreen to have it
- // turn on. Some devices want this because they don't have a
- // charging LED.
- synchronized (mLocks) {
- if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0 ||
- mUnplugTurnsOnScreen) {
- forceUserActivityLocked();
- }
- }
- }
- }
- }
- }
-
- private final class BootCompletedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- bootCompleted();
- }
- }
-
- private final class DockReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
- Intent.EXTRA_DOCK_STATE_UNDOCKED);
- dockStateChanged(state);
- }
- }
-
- /**
- * Set the setting that determines whether the device stays on when plugged in.
- * The argument is a bit string, with each bit specifying a power source that,
- * when the device is connected to that source, causes the device to stay on.
- * See {@link android.os.BatteryManager} for the list of power sources that
- * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC}
- * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB}
- * @param val an {@code int} containing the bits that specify which power sources
- * should cause the device to stay on.
- */
- public void setStayOnSetting(int val) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null);
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.STAY_ON_WHILE_PLUGGED_IN, val);
- }
-
- public void setMaximumScreenOffTimeount(int timeMs) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS, null);
- synchronized (mLocks) {
- mMaximumScreenOffTimeout = timeMs;
- // recalculate everything
- setScreenOffTimeoutsLocked();
- }
- }
-
- int getStayOnConditionsLocked() {
- return mMaximumScreenOffTimeout <= 0 || mMaximumScreenOffTimeout == Integer.MAX_VALUE
- ? mStayOnConditions : 0;
- }
-
- private class SettingsObserver implements Observer {
- private int getInt(String name, int defValue) {
- ContentValues values = mSettings.getValues(name);
- Integer iVal = values != null ? values.getAsInteger(Settings.System.VALUE) : null;
- return iVal != null ? iVal : defValue;
- }
-
- private float getFloat(String name, float defValue) {
- ContentValues values = mSettings.getValues(name);
- Float fVal = values != null ? values.getAsFloat(Settings.System.VALUE) : null;
- return fVal != null ? fVal : defValue;
- }
-
- public void update(Observable o, Object arg) {
- synchronized (mLocks) {
- // STAY_ON_WHILE_PLUGGED_IN, default to when plugged into AC
- mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN,
- BatteryManager.BATTERY_PLUGGED_AC);
- updateWakeLockLocked();
-
- // SCREEN_OFF_TIMEOUT, default to 15 seconds
- mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
-
- // DIM_SCREEN
- //mDimScreen = getInt(DIM_SCREEN) != 0;
-
- mScreenBrightnessSetting = getInt(SCREEN_BRIGHTNESS, DEFAULT_SCREEN_BRIGHTNESS);
- mLightSensorAdjustSetting = 0; //getFloat(SCREEN_AUTO_BRIGHTNESS_ADJ, 0);
-
- // SCREEN_BRIGHTNESS_MODE, default to manual
- setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL));
-
- // recalculate everything
- setScreenOffTimeoutsLocked();
-
- mWindowScaleAnimation = getFloat(WINDOW_ANIMATION_SCALE, 1.0f);
- final float transitionScale = getFloat(TRANSITION_ANIMATION_SCALE, 1.0f);
- mAnimationSetting = 0;
- if (mWindowScaleAnimation > 0.5f) {
- mAnimationSetting |= ANIM_SETTING_OFF;
- }
- if (transitionScale > 0.5f) {
- // Uncomment this if you want the screen-on animation.
- // mAnimationSetting |= ANIM_SETTING_ON;
- }
- }
- }
- }
-
- PowerManagerService() {
- // Hack to get our uid... should have a func for this.
- long token = Binder.clearCallingIdentity();
- MY_UID = Process.myUid();
- MY_PID = Process.myPid();
- Binder.restoreCallingIdentity(token);
-
- // assume nothing is on yet
- mUserState = mPowerState = 0;
-
- // Add ourself to the Watchdog monitors.
- Watchdog.getInstance().addMonitor(this);
-
- nativeInit();
- }
-
- private ContentQueryMap mSettings;
-
- void init(Context context, LightsService lights, IActivityManager activity,
- BatteryService battery) {
- mLightsService = lights;
- mContext = context;
- mActivityService = activity;
- mBatteryStats = BatteryStatsService.getService();
- mBatteryService = battery;
-
- mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);
- mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS);
- mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
- mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
- mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
-
- mInitComplete = false;
- mScreenBrightnessAnimator = new ScreenBrightnessAnimator("mScreenBrightnessUpdaterThread",
- Process.THREAD_PRIORITY_DISPLAY);
- mScreenBrightnessAnimator.start();
-
- synchronized (mScreenBrightnessAnimator) {
- while (!mInitComplete) {
- try {
- mScreenBrightnessAnimator.wait();
- } catch (InterruptedException e) {
- // Ignore
- }
- }
- }
-
- mInitComplete = false;
- mHandlerThread = new HandlerThread("PowerManagerService") {
- @Override
- protected void onLooperPrepared() {
- super.onLooperPrepared();
- initInThread();
- }
- };
- mHandlerThread.start();
-
- synchronized (mHandlerThread) {
- while (!mInitComplete) {
- try {
- mHandlerThread.wait();
- } catch (InterruptedException e) {
- // Ignore
- }
- }
- }
-
- synchronized (mLocks) {
- updateNativePowerStateLocked();
- // We make sure to start out with the screen on due to user activity.
- // (They did just boot their device, after all.)
- forceUserActivityLocked();
- mInitialized = true;
- }
- }
-
- void initInThread() {
- mHandler = new Handler();
-
- mBroadcastWakeLock = new UnsynchronizedWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "sleep_broadcast", true);
- mStayOnWhilePluggedInScreenDimLock = new UnsynchronizedWakeLock(
- PowerManager.SCREEN_DIM_WAKE_LOCK, "StayOnWhilePluggedIn Screen Dim", false);
- mStayOnWhilePluggedInPartialLock = new UnsynchronizedWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false);
- mPreventScreenOnPartialLock = new UnsynchronizedWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false);
- mProximityPartialLock = new UnsynchronizedWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false);
-
- mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
- mScreenOnIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
- mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
- mScreenOffIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
-
- Resources resources = mContext.getResources();
-
- mAnimateScreenLights = resources.getBoolean(
- com.android.internal.R.bool.config_animateScreenLights);
-
- mUnplugTurnsOnScreen = resources.getBoolean(
- com.android.internal.R.bool.config_unplugTurnsOnScreen);
-
- mScreenBrightnessDim = resources.getInteger(
- com.android.internal.R.integer.config_screenBrightnessDim);
-
- // read settings for auto-brightness
- mUseSoftwareAutoBrightness = resources.getBoolean(
- com.android.internal.R.bool.config_automatic_brightness_available);
- if (mUseSoftwareAutoBrightness) {
- mAutoBrightnessLevels = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLevels);
- mLcdBacklightValues = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
- mButtonBacklightValues = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessButtonBacklightValues);
- mKeyboardBacklightValues = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessKeyboardBacklightValues);
- mLightSensorWarmupTime = resources.getInteger(
- com.android.internal.R.integer.config_lightSensorWarmupTime);
- }
-
- ContentResolver resolver = mContext.getContentResolver();
- Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null,
- "(" + Settings.System.NAME + "=?) or ("
- + Settings.System.NAME + "=?) or ("
- + Settings.System.NAME + "=?) or ("
- + Settings.System.NAME + "=?) or ("
- + Settings.System.NAME + "=?) or ("
- + Settings.System.NAME + "=?) or ("
- + Settings.System.NAME + "=?) or ("
- + Settings.System.NAME + "=?)",
- new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN, SCREEN_BRIGHTNESS,
- SCREEN_BRIGHTNESS_MODE, /*SCREEN_AUTO_BRIGHTNESS_ADJ,*/
- WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
- null);
- mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler);
- SettingsObserver settingsObserver = new SettingsObserver();
- mSettings.addObserver(settingsObserver);
-
- // pretend that the settings changed so we will get their initial state
- settingsObserver.update(mSettings, null);
-
- // register for the battery changed notifications
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- mContext.registerReceiver(new BatteryReceiver(), filter);
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BOOT_COMPLETED);
- mContext.registerReceiver(new BootCompletedReceiver(), filter);
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_DOCK_EVENT);
- mContext.registerReceiver(new DockReceiver(), filter);
-
- // Listen for secure settings changes
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.CONTENT_URI, true,
- new ContentObserver(new Handler()) {
- public void onChange(boolean selfChange) {
- updateSettingsValues();
- }
- });
- updateSettingsValues();
-
- synchronized (mHandlerThread) {
- mInitComplete = true;
- mHandlerThread.notifyAll();
- }
- }
-
- /**
- * Low-level function turn the device off immediately, without trying
- * to be clean. Most people should use
- * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown.
- */
- public static void lowLevelShutdown() {
- nativeShutdown();
- }
-
- /**
- * Low-level function to reboot the device.
- *
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @throws IOException if reboot fails for some reason (eg, lack of
- * permission)
- */
- public static void lowLevelReboot(String reason) throws IOException {
- nativeReboot(reason);
- }
-
- private class WakeLock implements IBinder.DeathRecipient
- {
- WakeLock(int f, IBinder b, String t, int u, int p) {
- super();
- flags = f;
- binder = b;
- tag = t;
- uid = u == MY_UID ? Process.SYSTEM_UID : u;
- pid = p;
- if (u != MY_UID || (
- !"KEEP_SCREEN_ON_FLAG".equals(tag)
- && !"KeyInputQueue".equals(tag))) {
- monitorType = (f & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK
- ? BatteryStats.WAKE_TYPE_PARTIAL
- : BatteryStats.WAKE_TYPE_FULL;
- } else {
- monitorType = -1;
- }
- try {
- b.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
- }
- public void binderDied() {
- synchronized (mLocks) {
- releaseWakeLockLocked(this.binder, 0, true);
- }
- }
- final int flags;
- final IBinder binder;
- final String tag;
- final int uid;
- final int pid;
- final int monitorType;
- WorkSource ws;
- boolean activated = true;
- int minState;
- }
-
- private void updateWakeLockLocked() {
- final int stayOnConditions = getStayOnConditionsLocked();
- if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) {
- // keep the device on if we're plugged in and mStayOnWhilePluggedIn is set.
- mStayOnWhilePluggedInScreenDimLock.acquire();
- mStayOnWhilePluggedInPartialLock.acquire();
- } else {
- mStayOnWhilePluggedInScreenDimLock.release();
- mStayOnWhilePluggedInPartialLock.release();
- }
- }
-
- private boolean isScreenLock(int flags)
- {
- int n = flags & LOCK_MASK;
- return n == PowerManager.FULL_WAKE_LOCK
- || n == PowerManager.SCREEN_BRIGHT_WAKE_LOCK
- || n == PowerManager.SCREEN_DIM_WAKE_LOCK
- || n == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
- }
-
- void enforceWakeSourcePermission(int uid, int pid) {
- if (uid == Process.myUid()) {
- return;
- }
- mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
- pid, uid, null);
- }
-
- public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) {
- int uid = Binder.getCallingUid();
- int pid = Binder.getCallingPid();
- if (uid != Process.myUid()) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- }
- if (ws != null) {
- enforceWakeSourcePermission(uid, pid);
- }
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, pid, tag, ws);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- void noteStartWakeLocked(WakeLock wl, WorkSource ws) {
- if (wl.monitorType >= 0) {
- long origId = Binder.clearCallingIdentity();
- try {
- if (ws != null) {
- mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag,
- wl.monitorType);
- } else {
- mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
- }
- } catch (RemoteException e) {
- // Ignore
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- void noteStopWakeLocked(WakeLock wl, WorkSource ws) {
- if (wl.monitorType >= 0) {
- long origId = Binder.clearCallingIdentity();
- try {
- if (ws != null) {
- mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag,
- wl.monitorType);
- } else {
- mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
- }
- } catch (RemoteException e) {
- // Ignore
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag,
- WorkSource ws) {
- if (mSpew) {
- Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag);
- }
-
- if (ws != null && ws.size() == 0) {
- ws = null;
- }
-
- int index = mLocks.getIndex(lock);
- WakeLock wl;
- boolean newlock;
- boolean diffsource;
- WorkSource oldsource;
- if (index < 0) {
- wl = new WakeLock(flags, lock, tag, uid, pid);
- switch (wl.flags & LOCK_MASK)
- {
- case PowerManager.FULL_WAKE_LOCK:
- if (mUseSoftwareAutoBrightness) {
- wl.minState = SCREEN_BRIGHT;
- } else {
- wl.minState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
- }
- break;
- case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
- wl.minState = SCREEN_BRIGHT;
- break;
- case PowerManager.SCREEN_DIM_WAKE_LOCK:
- wl.minState = SCREEN_DIM;
- break;
- case PowerManager.PARTIAL_WAKE_LOCK:
- case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
- break;
- default:
- // just log and bail. we're in the server, so don't
- // throw an exception.
- Slog.e(TAG, "bad wakelock type for lock '" + tag + "' "
- + " flags=" + flags);
- return;
- }
- mLocks.addLock(wl);
- if (ws != null) {
- wl.ws = new WorkSource(ws);
- }
- newlock = true;
- diffsource = false;
- oldsource = null;
- } else {
- wl = mLocks.get(index);
- newlock = false;
- oldsource = wl.ws;
- if (oldsource != null) {
- if (ws == null) {
- wl.ws = null;
- diffsource = true;
- } else {
- diffsource = oldsource.diff(ws);
- }
- } else if (ws != null) {
- diffsource = true;
- } else {
- diffsource = false;
- }
- if (diffsource) {
- wl.ws = new WorkSource(ws);
- }
- }
- if (isScreenLock(flags)) {
- // if this causes a wakeup, we reactivate all of the locks and
- // set it to whatever they want. otherwise, we modulate that
- // by the current state so we never turn it more on than
- // it already is.
- if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
- mProximityWakeLockCount++;
- if (mProximityWakeLockCount == 1) {
- enableProximityLockLocked();
- }
- } else {
- if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
- int oldWakeLockState = mWakeLockState;
- mWakeLockState = mLocks.reactivateScreenLocksLocked();
-
- // Disable proximity sensor if if user presses power key while we are in the
- // "waiting for proximity sensor to go negative" state.
- if ((mWakeLockState & SCREEN_ON_BIT) != 0
- && mProximitySensorActive && mProximityWakeLockCount == 0) {
- mProximitySensorActive = false;
- }
-
- if (mSpew) {
- Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
- + " mWakeLockState=0x"
- + Integer.toHexString(mWakeLockState)
- + " previous wakeLockState=0x"
- + Integer.toHexString(oldWakeLockState));
- }
- } else {
- if (mSpew) {
- Slog.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState)
- + " mLocks.gatherState()=0x"
- + Integer.toHexString(mLocks.gatherState())
- + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
- }
- mWakeLockState = (mUserState | mWakeLockState) & mLocks.gatherState();
- }
- setPowerState(mWakeLockState | mUserState);
- }
- }
- else if ((flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
- if (newlock) {
- mPartialCount++;
- if (mPartialCount == 1) {
- if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 1, tag);
- }
- }
- nativeAcquireWakeLock(PARTIAL_WAKE_LOCK_ID, PARTIAL_NAME);
- }
-
- if (diffsource) {
- // If the lock sources have changed, need to first release the
- // old ones.
- noteStopWakeLocked(wl, oldsource);
- }
- if (newlock || diffsource) {
- noteStartWakeLocked(wl, ws);
- }
- }
-
- public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
- int uid = Binder.getCallingUid();
- int pid = Binder.getCallingPid();
- if (ws != null && ws.size() == 0) {
- ws = null;
- }
- if (ws != null) {
- enforceWakeSourcePermission(uid, pid);
- }
- synchronized (mLocks) {
- int index = mLocks.getIndex(lock);
- if (index < 0) {
- throw new IllegalArgumentException("Wake lock not active");
- }
- WakeLock wl = mLocks.get(index);
- WorkSource oldsource = wl.ws;
- wl.ws = ws != null ? new WorkSource(ws) : null;
- noteStopWakeLocked(wl, oldsource);
- noteStartWakeLocked(wl, ws);
- }
- }
-
- public void releaseWakeLock(IBinder lock, int flags) {
- int uid = Binder.getCallingUid();
- if (uid != Process.myUid()) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- }
-
- synchronized (mLocks) {
- releaseWakeLockLocked(lock, flags, false);
- }
- }
-
- private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
- WakeLock wl = mLocks.removeLock(lock);
- if (wl == null) {
- return;
- }
-
- if (mSpew) {
- Slog.d(TAG, "releaseWakeLock flags=0x"
- + Integer.toHexString(wl.flags) + " tag=" + wl.tag);
- }
-
- if (isScreenLock(wl.flags)) {
- if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
- mProximityWakeLockCount--;
- if (mProximityWakeLockCount == 0) {
- if (mProximitySensorActive &&
- ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0)) {
- // wait for proximity sensor to go negative before disabling sensor
- if (mDebugProximitySensor) {
- Slog.d(TAG, "waiting for proximity sensor to go negative");
- }
- } else {
- disableProximityLockLocked();
- }
- }
- } else {
- mWakeLockState = mLocks.gatherState();
- // goes in the middle to reduce flicker
- if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) {
- userActivity(SystemClock.uptimeMillis(), -1, false, OTHER_EVENT, false, true);
- }
- setPowerState(mWakeLockState | mUserState);
- }
- }
- else if ((wl.flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
- mPartialCount--;
- if (mPartialCount == 0) {
- if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
- nativeReleaseWakeLock(PARTIAL_NAME);
- }
- }
- // Unlink the lock from the binder.
- wl.binder.unlinkToDeath(wl, 0);
-
- noteStopWakeLocked(wl, wl.ws);
- }
-
- private class PokeLock implements IBinder.DeathRecipient
- {
- PokeLock(int p, IBinder b, String t) {
- super();
- this.pokey = p;
- this.binder = b;
- this.tag = t;
- try {
- b.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
- }
- public void binderDied() {
- setPokeLock(0, this.binder, this.tag);
- }
- int pokey;
- IBinder binder;
- String tag;
- boolean awakeOnSet;
- }
-
- public void setPokeLock(int pokey, IBinder token, String tag) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- if (token == null) {
- Slog.e(TAG, "setPokeLock got null token for tag='" + tag + "'");
- return;
- }
-
- if ((pokey & POKE_LOCK_TIMEOUT_MASK) == POKE_LOCK_TIMEOUT_MASK) {
- throw new IllegalArgumentException("setPokeLock can't have both POKE_LOCK_SHORT_TIMEOUT"
- + " and POKE_LOCK_MEDIUM_TIMEOUT");
- }
-
- synchronized (mLocks) {
- if (pokey != 0) {
- PokeLock p = mPokeLocks.get(token);
- int oldPokey = 0;
- if (p != null) {
- oldPokey = p.pokey;
- p.pokey = pokey;
- } else {
- p = new PokeLock(pokey, token, tag);
- mPokeLocks.put(token, p);
- }
- int oldTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK;
- int newTimeout = pokey & POKE_LOCK_TIMEOUT_MASK;
- if (((mPowerState & SCREEN_ON_BIT) == 0) && (oldTimeout != newTimeout)) {
- p.awakeOnSet = true;
- }
- } else {
- PokeLock rLock = mPokeLocks.remove(token);
- if (rLock != null) {
- token.unlinkToDeath(rLock, 0);
- }
- }
-
- int oldPokey = mPokey;
- int cumulative = 0;
- boolean awakeOnSet = false;
- for (PokeLock p: mPokeLocks.values()) {
- cumulative |= p.pokey;
- if (p.awakeOnSet) {
- awakeOnSet = true;
- }
- }
- mPokey = cumulative;
- mPokeAwakeOnSet = awakeOnSet;
-
- int oldCumulativeTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK;
- int newCumulativeTimeout = pokey & POKE_LOCK_TIMEOUT_MASK;
-
- if (oldCumulativeTimeout != newCumulativeTimeout) {
- setScreenOffTimeoutsLocked();
- // reset the countdown timer, but use the existing nextState so it doesn't
- // change anything
- setTimeoutLocked(SystemClock.uptimeMillis(), mTimeoutTask.nextState);
- }
- }
- }
-
- private static String lockType(int type)
- {
- switch (type)
- {
- case PowerManager.FULL_WAKE_LOCK:
- return "FULL_WAKE_LOCK ";
- case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
- return "SCREEN_BRIGHT_WAKE_LOCK ";
- case PowerManager.SCREEN_DIM_WAKE_LOCK:
- return "SCREEN_DIM_WAKE_LOCK ";
- case PowerManager.PARTIAL_WAKE_LOCK:
- return "PARTIAL_WAKE_LOCK ";
- case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
- return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
- default:
- return "??? ";
- }
- }
-
- private static String dumpPowerState(int state) {
- return (((state & KEYBOARD_BRIGHT_BIT) != 0)
- ? "KEYBOARD_BRIGHT_BIT " : "")
- + (((state & SCREEN_BRIGHT_BIT) != 0)
- ? "SCREEN_BRIGHT_BIT " : "")
- + (((state & SCREEN_ON_BIT) != 0)
- ? "SCREEN_ON_BIT " : "")
- + (((state & BUTTON_BRIGHT_BIT) != 0)
- ? "BUTTON_BRIGHT_BIT " : "")
- + (((state & BATTERY_LOW_BIT) != 0)
- ? "BATTERY_LOW_BIT " : "");
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump PowerManager from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- long now = SystemClock.uptimeMillis();
-
- synchronized (mLocks) {
- pw.println("Power Manager State:");
- pw.println(" mIsPowered=" + mIsPowered
- + " mPowerState=" + mPowerState
- + " mScreenOffTime=" + (SystemClock.elapsedRealtime()-mScreenOffTime)
- + " ms");
- pw.println(" mPartialCount=" + mPartialCount);
- pw.println(" mWakeLockState=" + dumpPowerState(mWakeLockState));
- pw.println(" mUserState=" + dumpPowerState(mUserState));
- pw.println(" mPowerState=" + dumpPowerState(mPowerState));
- pw.println(" mLocks.gather=" + dumpPowerState(mLocks.gatherState()));
- pw.println(" mNextTimeout=" + mNextTimeout + " now=" + now
- + " " + ((mNextTimeout-now)/1000) + "s from now");
- pw.println(" mDimScreen=" + mDimScreen
- + " mStayOnConditions=" + mStayOnConditions
- + " mPreparingForScreenOn=" + mPreparingForScreenOn
- + " mSkippedScreenOn=" + mSkippedScreenOn);
- pw.println(" mScreenOffReason=" + mScreenOffReason
- + " mUserState=" + mUserState);
- pw.println(" mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1]
- + ',' + mBroadcastQueue[2] + "}");
- pw.println(" mBroadcastWhy={" + mBroadcastWhy[0] + ',' + mBroadcastWhy[1]
- + ',' + mBroadcastWhy[2] + "}");
- pw.println(" mPokey=" + mPokey + " mPokeAwakeonSet=" + mPokeAwakeOnSet);
- pw.println(" mKeyboardVisible=" + mKeyboardVisible
- + " mUserActivityAllowed=" + mUserActivityAllowed);
- pw.println(" mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay
- + " mScreenOffDelay=" + mScreenOffDelay);
- pw.println(" mPreventScreenOn=" + mPreventScreenOn
- + " mScreenBrightnessOverride=" + mScreenBrightnessOverride
- + " mButtonBrightnessOverride=" + mButtonBrightnessOverride);
- pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting
- + " mMaximumScreenOffTimeout=" + mMaximumScreenOffTimeout);
- pw.println(" mLastScreenOnTime=" + mLastScreenOnTime);
- pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock);
- pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
- pw.println(" mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
- pw.println(" mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
- pw.println(" mProximityPartialLock=" + mProximityPartialLock);
- pw.println(" mProximityWakeLockCount=" + mProximityWakeLockCount);
- pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
- pw.println(" mProximitySensorActive=" + mProximitySensorActive);
- pw.println(" mProximityPendingValue=" + mProximityPendingValue);
- pw.println(" mLastProximityEventTime=" + mLastProximityEventTime);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled
- + " mLightSensorAdjustSetting=" + mLightSensorAdjustSetting);
- pw.println(" mLightSensorValue=" + mLightSensorValue
- + " mLightSensorPendingValue=" + mLightSensorPendingValue);
- pw.println(" mHighestLightSensorValue=" + mHighestLightSensorValue
- + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor);
- pw.println(" mLightSensorPendingDecrease=" + mLightSensorPendingDecrease
- + " mLightSensorPendingIncrease=" + mLightSensorPendingIncrease);
- pw.println(" mLightSensorScreenBrightness=" + mLightSensorScreenBrightness
- + " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness
- + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness);
- pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness);
- pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled);
- mScreenBrightnessAnimator.dump(pw, "mScreenBrightnessAnimator: ");
-
- int N = mLocks.size();
- pw.println();
- pw.println("mLocks.size=" + N + ":");
- for (int i=0; i<N; i++) {
- WakeLock wl = mLocks.get(i);
- String type = lockType(wl.flags & LOCK_MASK);
- String acquireCausesWakeup = "";
- if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
- acquireCausesWakeup = "ACQUIRE_CAUSES_WAKEUP ";
- }
- String activated = "";
- if (wl.activated) {
- activated = " activated";
- }
- pw.println(" " + type + " '" + wl.tag + "'" + acquireCausesWakeup
- + activated + " (minState=" + wl.minState + ", uid=" + wl.uid
- + ", pid=" + wl.pid + ")");
- }
-
- pw.println();
- pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":");
- for (PokeLock p: mPokeLocks.values()) {
- pw.println(" poke lock '" + p.tag + "':"
- + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0
- ? " POKE_LOCK_IGNORE_TOUCH_EVENTS" : "")
- + ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0
- ? " POKE_LOCK_SHORT_TIMEOUT" : "")
- + ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0
- ? " POKE_LOCK_MEDIUM_TIMEOUT" : ""));
- }
-
- pw.println();
- }
- }
-
- private void setTimeoutLocked(long now, int nextState) {
- setTimeoutLocked(now, -1, nextState);
- }
-
- // If they gave a timeoutOverride it is the number of seconds
- // to screen-off. Figure out where in the countdown cycle we
- // should jump to.
- private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) {
- long timeoutOverride = originalTimeoutOverride;
- if (mBootCompleted) {
- synchronized (mLocks) {
- long when = 0;
- if (timeoutOverride <= 0) {
- switch (nextState)
- {
- case SCREEN_BRIGHT:
- when = now + mKeylightDelay;
- break;
- case SCREEN_DIM:
- if (mDimDelay >= 0) {
- when = now + mDimDelay;
- break;
- } else {
- Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
- }
- case SCREEN_OFF:
- synchronized (mLocks) {
- when = now + mScreenOffDelay;
- }
- break;
- default:
- when = now;
- break;
- }
- } else {
- override: {
- if (timeoutOverride <= mScreenOffDelay) {
- when = now + timeoutOverride;
- nextState = SCREEN_OFF;
- break override;
- }
- timeoutOverride -= mScreenOffDelay;
-
- if (mDimDelay >= 0) {
- if (timeoutOverride <= mDimDelay) {
- when = now + timeoutOverride;
- nextState = SCREEN_DIM;
- break override;
- }
- timeoutOverride -= mDimDelay;
- }
-
- when = now + timeoutOverride;
- nextState = SCREEN_BRIGHT;
- }
- }
- if (mSpew) {
- Slog.d(TAG, "setTimeoutLocked now=" + now
- + " timeoutOverride=" + timeoutOverride
- + " nextState=" + nextState + " when=" + when);
- }
-
- mHandler.removeCallbacks(mTimeoutTask);
- mTimeoutTask.nextState = nextState;
- mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0
- ? (originalTimeoutOverride - timeoutOverride)
- : -1;
- mHandler.postAtTime(mTimeoutTask, when);
- mNextTimeout = when; // for debugging
- }
- }
- }
-
- private void cancelTimerLocked()
- {
- mHandler.removeCallbacks(mTimeoutTask);
- mTimeoutTask.nextState = -1;
- }
-
- private class TimeoutTask implements Runnable
- {
- int nextState; // access should be synchronized on mLocks
- long remainingTimeoutOverride;
- public void run()
- {
- synchronized (mLocks) {
- if (mSpew) {
- Slog.d(TAG, "user activity timeout timed out nextState=" + this.nextState);
- }
-
- if (nextState == -1) {
- return;
- }
-
- mUserState = this.nextState;
- setPowerState(this.nextState | mWakeLockState);
-
- long now = SystemClock.uptimeMillis();
-
- switch (this.nextState)
- {
- case SCREEN_BRIGHT:
- if (mDimDelay >= 0) {
- setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);
- break;
- }
- case SCREEN_DIM:
- setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);
- break;
- }
- }
- }
- }
-
- private void sendNotificationLocked(boolean on, int why) {
- if (!mInitialized) {
- // No notifications sent until first initialization is done.
- // This is so that when we are moving from our initial state
- // which looks like the screen was off to it being on, we do not
- // go through the process of waiting for the higher-level user
- // space to be ready before turning up the display brightness.
- // (And also do not send needless broadcasts about the screen.)
- return;
- }
-
- if (DEBUG_SCREEN_ON) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "sendNotificationLocked: " + on, here);
- }
-
- if (!on) {
- mStillNeedSleepNotification = false;
- }
-
- // Add to the queue.
- int index = 0;
- while (mBroadcastQueue[index] != -1) {
- index++;
- }
- mBroadcastQueue[index] = on ? 1 : 0;
- mBroadcastWhy[index] = why;
-
- // If we added it position 2, then there is a pair that can be stripped.
- // If we added it position 1 and we're turning the screen off, we can strip
- // the pair and do nothing, because the screen is already off, and therefore
- // keyguard has already been enabled.
- // However, if we added it at position 1 and we're turning it on, then position
- // 0 was to turn it off, and we can't strip that, because keyguard needs to come
- // on, so have to run the queue then.
- if (index == 2) {
- // While we're collapsing them, if it's going off, and the new reason
- // is more significant than the first, then use the new one.
- if (!on && mBroadcastWhy[0] > why) {
- mBroadcastWhy[0] = why;
- }
- mBroadcastQueue[0] = on ? 1 : 0;
- mBroadcastQueue[1] = -1;
- mBroadcastQueue[2] = -1;
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
- mBroadcastWakeLock.release();
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
- mBroadcastWakeLock.release();
- index = 0;
- }
- if (index == 1 && !on) {
- mBroadcastQueue[0] = -1;
- mBroadcastQueue[1] = -1;
- index = -1;
- // The wake lock was being held, but we're not actually going to do any
- // broadcasts, so release the wake lock.
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
- mBroadcastWakeLock.release();
- }
-
- // The broadcast queue has changed; make sure the screen is on if it
- // is now possible for it to be.
- if (mSkippedScreenOn) {
- updateLightsLocked(mPowerState, SCREEN_ON_BIT);
- }
-
- // Now send the message.
- if (index >= 0) {
- // Acquire the broadcast wake lock before changing the power
- // state. It will be release after the broadcast is sent.
- // We always increment the ref count for each notification in the queue
- // and always decrement when that notification is handled.
- mBroadcastWakeLock.acquire();
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount);
- mHandler.post(mNotificationTask);
- }
- }
-
- private WindowManagerPolicy.ScreenOnListener mScreenOnListener =
- new WindowManagerPolicy.ScreenOnListener() {
- public void onScreenOn() {
- synchronized (mLocks) {
- if (mPreparingForScreenOn) {
- mPreparingForScreenOn = false;
- updateLightsLocked(mPowerState, SCREEN_ON_BIT);
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP,
- 4, mBroadcastWakeLock.mCount);
- mBroadcastWakeLock.release();
- }
- }
- }
- };
-
- private Runnable mNotificationTask = new Runnable()
- {
- public void run()
- {
- while (true) {
- int value;
- int why;
- WindowManagerPolicy policy;
- synchronized (mLocks) {
- value = mBroadcastQueue[0];
- why = mBroadcastWhy[0];
- for (int i=0; i<2; i++) {
- mBroadcastQueue[i] = mBroadcastQueue[i+1];
- mBroadcastWhy[i] = mBroadcastWhy[i+1];
- }
- policy = getPolicyLocked();
- if (value == 1 && !mPreparingForScreenOn) {
- mPreparingForScreenOn = true;
- mBroadcastWakeLock.acquire();
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND,
- mBroadcastWakeLock.mCount);
- }
- }
- if (value == 1) {
- mScreenOnStart = SystemClock.uptimeMillis();
-
- policy.screenTurningOn(mScreenOnListener);
- try {
- ActivityManagerNative.getDefault().wakingUp();
- } catch (RemoteException e) {
- // ignore it
- }
-
- if (mSpew) {
- Slog.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock);
- }
- if (mContext != null && ActivityManagerNative.isSystemReady()) {
- mContext.sendOrderedBroadcast(mScreenOnIntent, null,
- mScreenOnBroadcastDone, mHandler, 0, null, null);
- } else {
- synchronized (mLocks) {
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2,
- mBroadcastWakeLock.mCount);
- mBroadcastWakeLock.release();
- }
- }
- }
- else if (value == 0) {
- mScreenOffStart = SystemClock.uptimeMillis();
-
- policy.screenTurnedOff(why);
- try {
- ActivityManagerNative.getDefault().goingToSleep();
- } catch (RemoteException e) {
- // ignore it.
- }
-
- if (mContext != null && ActivityManagerNative.isSystemReady()) {
- mContext.sendOrderedBroadcast(mScreenOffIntent, null,
- mScreenOffBroadcastDone, mHandler, 0, null, null);
- } else {
- synchronized (mLocks) {
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3,
- mBroadcastWakeLock.mCount);
- updateLightsLocked(mPowerState, SCREEN_ON_BIT);
- mBroadcastWakeLock.release();
- }
- }
- }
- else {
- // If we're in this case, then this handler is running for a previous
- // paired transaction. mBroadcastWakeLock will already have been released.
- break;
- }
- }
- }
- };
-
- long mScreenOnStart;
- private BroadcastReceiver mScreenOnBroadcastDone = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- synchronized (mLocks) {
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
- SystemClock.uptimeMillis() - mScreenOnStart, mBroadcastWakeLock.mCount);
- mBroadcastWakeLock.release();
- }
- }
- };
-
- long mScreenOffStart;
- private BroadcastReceiver mScreenOffBroadcastDone = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- synchronized (mLocks) {
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
- SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount);
- mBroadcastWakeLock.release();
- }
- }
- };
-
- void logPointerUpEvent() {
- if (LOG_TOUCH_DOWNS) {
- mTotalTouchDownTime += SystemClock.elapsedRealtime() - mLastTouchDown;
- mLastTouchDown = 0;
- }
- }
-
- void logPointerDownEvent() {
- if (LOG_TOUCH_DOWNS) {
- // If we are not already timing a down/up sequence
- if (mLastTouchDown == 0) {
- mLastTouchDown = SystemClock.elapsedRealtime();
- mTouchCycles++;
- }
- }
- }
-
- /**
- * Prevents the screen from turning on even if it *should* turn on due
- * to a subsequent full wake lock being acquired.
- * <p>
- * This is a temporary hack that allows an activity to "cover up" any
- * display glitches that happen during the activity's startup
- * sequence. (Specifically, this API was added to work around a
- * cosmetic bug in the "incoming call" sequence, where the lock screen
- * would flicker briefly before the incoming call UI became visible.)
- * TODO: There ought to be a more elegant way of doing this,
- * probably by having the PowerManager and ActivityManager
- * work together to let apps specify that the screen on/off
- * state should be synchronized with the Activity lifecycle.
- * <p>
- * Note that calling preventScreenOn(true) will NOT turn the screen
- * off if it's currently on. (This API only affects *future*
- * acquisitions of full wake locks.)
- * But calling preventScreenOn(false) WILL turn the screen on if
- * it's currently off because of a prior preventScreenOn(true) call.
- * <p>
- * Any call to preventScreenOn(true) MUST be followed promptly by a call
- * to preventScreenOn(false). In fact, if the preventScreenOn(false)
- * call doesn't occur within 5 seconds, we'll turn the screen back on
- * ourselves (and log a warning about it); this prevents a buggy app
- * from disabling the screen forever.)
- * <p>
- * TODO: this feature should really be controlled by a new type of poke
- * lock (rather than an IPowerManager call).
- */
- public void preventScreenOn(boolean prevent) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-
- synchronized (mLocks) {
- if (prevent) {
- // First of all, grab a partial wake lock to
- // make sure the CPU stays on during the entire
- // preventScreenOn(true) -> preventScreenOn(false) sequence.
- mPreventScreenOnPartialLock.acquire();
-
- // Post a forceReenableScreen() call (for 5 seconds in the
- // future) to make sure the matching preventScreenOn(false) call
- // has happened by then.
- mHandler.removeCallbacks(mForceReenableScreenTask);
- mHandler.postDelayed(mForceReenableScreenTask, 5000);
-
- // Finally, set the flag that prevents the screen from turning on.
- // (Below, in setPowerState(), we'll check mPreventScreenOn and
- // we *won't* call setScreenStateLocked(true) if it's set.)
- mPreventScreenOn = true;
- } else {
- // (Re)enable the screen.
- mPreventScreenOn = false;
-
- // We're "undoing" a the prior preventScreenOn(true) call, so we
- // no longer need the 5-second safeguard.
- mHandler.removeCallbacks(mForceReenableScreenTask);
-
- // Forcibly turn on the screen if it's supposed to be on. (This
- // handles the case where the screen is currently off because of
- // a prior preventScreenOn(true) call.)
- if (!mProximitySensorActive && (mPowerState & SCREEN_ON_BIT) != 0) {
- if (mSpew) {
- Slog.d(TAG,
- "preventScreenOn: turning on after a prior preventScreenOn(true)!");
- }
- int err = setScreenStateLocked(true);
- if (err != 0) {
- Slog.w(TAG, "preventScreenOn: error from setScreenStateLocked(): " + err);
- }
- }
-
- // Release the partial wake lock that we held during the
- // preventScreenOn(true) -> preventScreenOn(false) sequence.
- mPreventScreenOnPartialLock.release();
- }
- }
- }
-
- public void setScreenBrightnessOverride(int brightness) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-
- if (mSpew) Slog.d(TAG, "setScreenBrightnessOverride " + brightness);
- synchronized (mLocks) {
- if (mScreenBrightnessOverride != brightness) {
- mScreenBrightnessOverride = brightness;
- if (isScreenOn()) {
- updateLightsLocked(mPowerState, SCREEN_ON_BIT);
- }
- }
- }
- }
-
- public void setButtonBrightnessOverride(int brightness) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-
- if (mSpew) Slog.d(TAG, "setButtonBrightnessOverride " + brightness);
- synchronized (mLocks) {
- if (mButtonBrightnessOverride != brightness) {
- mButtonBrightnessOverride = brightness;
- if (isScreenOn()) {
- updateLightsLocked(mPowerState, BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT);
- }
- }
- }
- }
-
- /**
- * Sanity-check that gets called 5 seconds after any call to
- * preventScreenOn(true). This ensures that the original call
- * is followed promptly by a call to preventScreenOn(false).
- */
- private void forceReenableScreen() {
- // We shouldn't get here at all if mPreventScreenOn is false, since
- // we should have already removed any existing
- // mForceReenableScreenTask messages...
- if (!mPreventScreenOn) {
- Slog.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do");
- return;
- }
-
- // Uh oh. It's been 5 seconds since a call to
- // preventScreenOn(true) and we haven't re-enabled the screen yet.
- // This means the app that called preventScreenOn(true) is either
- // slow (i.e. it took more than 5 seconds to call preventScreenOn(false)),
- // or buggy (i.e. it forgot to call preventScreenOn(false), or
- // crashed before doing so.)
-
- // Log a warning, and forcibly turn the screen back on.
- Slog.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! "
- + "Forcing the screen back on...");
- preventScreenOn(false);
- }
-
- private Runnable mForceReenableScreenTask = new Runnable() {
- public void run() {
- forceReenableScreen();
- }
- };
-
- private int setScreenStateLocked(boolean on) {
- if (DEBUG_SCREEN_ON) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.i(TAG, "Set screen state: " + on, e);
- }
- if (on) {
- if (mInitialized && ((mPowerState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn)) {
- // If we are turning the screen state on, but the screen
- // light is currently off, then make sure that we set the
- // light at this point to 0. This is the case where we are
- // turning on the screen and waiting for the UI to be drawn
- // before showing it to the user. We want the light off
- // until it is ready to be shown to the user, not it using
- // whatever the last value it had.
- // Skip this if the screen is being turned on for the first time
- // after boot (mInitialized is false).
- if (DEBUG_SCREEN_ON) {
- Slog.i(TAG, "Forcing brightness 0: mPowerState=0x"
- + Integer.toHexString(mPowerState)
- + " mSkippedScreenOn=" + mSkippedScreenOn);
- }
- mScreenBrightnessAnimator.animateTo(PowerManager.BRIGHTNESS_OFF, SCREEN_BRIGHT_BIT, 0);
- }
- }
- int err = nativeSetScreenState(on);
- if (err == 0) {
- mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
- if (mUseSoftwareAutoBrightness) {
- enableLightSensorLocked(on);
- if (on) {
- // If AutoBrightness is enabled, set the brightness immediately after the
- // next sensor value is received.
- mWaitingForFirstLightSensor = mAutoBrightessEnabled;
- } else {
- // make sure button and key backlights are off too
- mButtonLight.turnOff();
- mKeyboardLight.turnOff();
- }
- }
- }
- return err;
- }
-
- private void setPowerState(int state)
- {
- setPowerState(state, false, WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT);
- }
-
- private void setPowerState(int newState, boolean noChangeLights, int reason)
- {
- synchronized (mLocks) {
- int err;
-
- if (mSpew) {
- Slog.d(TAG, "setPowerState: mPowerState=0x" + Integer.toHexString(mPowerState)
- + " newState=0x" + Integer.toHexString(newState)
- + " noChangeLights=" + noChangeLights
- + " reason=" + reason);
- }
-
- if (noChangeLights) {
- newState = (newState & ~LIGHTS_MASK) | (mPowerState & LIGHTS_MASK);
- }
- if (mProximitySensorActive) {
- // don't turn on the screen when the proximity sensor lock is held
- newState = (newState & ~SCREEN_BRIGHT);
- }
-
- if (batteryIsLow()) {
- newState |= BATTERY_LOW_BIT;
- } else {
- newState &= ~BATTERY_LOW_BIT;
- }
- if (newState == mPowerState && mInitialized) {
- return;
- }
-
- if (!mBootCompleted && !mUseSoftwareAutoBrightness) {
- newState |= ALL_BRIGHT;
- }
-
- boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0;
- boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0;
-
- if (mSpew) {
- Slog.d(TAG, "setPowerState: mPowerState=" + mPowerState
- + " newState=" + newState + " noChangeLights=" + noChangeLights);
- Slog.d(TAG, " oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0)
- + " newKeyboardBright=" + ((newState & KEYBOARD_BRIGHT_BIT) != 0));
- Slog.d(TAG, " oldScreenBright=" + ((mPowerState & SCREEN_BRIGHT_BIT) != 0)
- + " newScreenBright=" + ((newState & SCREEN_BRIGHT_BIT) != 0));
- Slog.d(TAG, " oldButtonBright=" + ((mPowerState & BUTTON_BRIGHT_BIT) != 0)
- + " newButtonBright=" + ((newState & BUTTON_BRIGHT_BIT) != 0));
- Slog.d(TAG, " oldScreenOn=" + oldScreenOn
- + " newScreenOn=" + newScreenOn);
- Slog.d(TAG, " oldBatteryLow=" + ((mPowerState & BATTERY_LOW_BIT) != 0)
- + " newBatteryLow=" + ((newState & BATTERY_LOW_BIT) != 0));
- }
-
- final boolean stateChanged = mPowerState != newState;
-
- if (stateChanged && reason == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT) {
- if (mPolicy != null && mPolicy.isScreenSaverEnabled()) {
- if (mSpew) {
- Slog.d(TAG, "setPowerState: running screen saver instead of turning off screen");
- }
- if (mPolicy.startScreenSaver()) {
- // was successful
- return;
- }
- }
- }
-
-
- if (oldScreenOn != newScreenOn) {
- if (newScreenOn) {
- // When the user presses the power button, we need to always send out the
- // notification that it's going to sleep so the keyguard goes on. But
- // we can't do that until the screen fades out, so we don't show the keyguard
- // too early.
- if (mStillNeedSleepNotification) {
- sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- }
-
- // Turn on the screen UNLESS there was a prior
- // preventScreenOn(true) request. (Note that the lifetime
- // of a single preventScreenOn() request is limited to 5
- // seconds to prevent a buggy app from disabling the
- // screen forever; see forceReenableScreen().)
- boolean reallyTurnScreenOn = true;
- if (mSpew) {
- Slog.d(TAG, "- turning screen on... mPreventScreenOn = "
- + mPreventScreenOn);
- }
-
- if (mPreventScreenOn) {
- if (mSpew) {
- Slog.d(TAG, "- PREVENTING screen from really turning on!");
- }
- reallyTurnScreenOn = false;
- }
- if (reallyTurnScreenOn) {
- err = setScreenStateLocked(true);
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(getPreferredBrightness());
- mBatteryStats.noteScreenOn();
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else {
- setScreenStateLocked(false);
- // But continue as if we really did turn the screen on...
- err = 0;
- }
-
- mLastTouchDown = 0;
- mTotalTouchDownTime = 0;
- mTouchCycles = 0;
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, reason,
- mTotalTouchDownTime, mTouchCycles);
- if (err == 0) {
- sendNotificationLocked(true, -1);
- // Update the lights *after* taking care of turning the
- // screen on, so we do this after our notifications are
- // enqueued and thus will delay turning on the screen light
- // until the windows are correctly displayed.
- if (stateChanged) {
- updateLightsLocked(newState, 0);
- }
- mPowerState |= SCREEN_ON_BIT;
- }
-
- } else {
- // Update the lights *before* taking care of turning the
- // screen off, so we can initiate any animations that are desired.
- mScreenOffReason = reason;
- if (stateChanged) {
- updateLightsLocked(newState, 0);
- }
-
- // cancel light sensor task
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = false;
- mLightSensorPendingIncrease = false;
- mScreenOffTime = SystemClock.elapsedRealtime();
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenOff();
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- mPowerState &= ~SCREEN_ON_BIT;
- if (!mScreenBrightnessAnimator.isAnimating()) {
- err = screenOffFinishedAnimatingLocked(reason);
- } else {
- err = 0;
- mLastTouchDown = 0;
- }
- }
- } else if (stateChanged) {
- // Screen on/off didn't change, but lights may have.
- updateLightsLocked(newState, 0);
- }
-
- mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK);
-
- updateNativePowerStateLocked();
- }
- }
-
- private void updateNativePowerStateLocked() {
- if (!mHeadless) {
- nativeSetPowerState(
- (mPowerState & SCREEN_ON_BIT) != 0,
- (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
- }
- }
-
- private int screenOffFinishedAnimatingLocked(int reason) {
- // I don't think we need to check the current state here because all of these
- // Power.setScreenState and sendNotificationLocked can both handle being
- // called multiple times in the same state. -joeo
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime,
- mTouchCycles);
- mLastTouchDown = 0;
- int err = setScreenStateLocked(false);
- if (err == 0) {
- mScreenOffReason = reason;
- sendNotificationLocked(false, reason);
- }
- return err;
- }
-
- private boolean batteryIsLow() {
- return (!mIsPowered &&
- mBatteryService.getBatteryLevel() <= LOW_BATTERY_THRESHOLD);
- }
-
- private boolean shouldDeferScreenOnLocked() {
- if (mPreparingForScreenOn) {
- // Currently waiting for confirmation from the policy that it
- // is okay to turn on the screen. Don't allow the screen to go
- // on until that is done.
- if (DEBUG_SCREEN_ON) Slog.i(TAG,
- "updateLights: delaying screen on due to mPreparingForScreenOn");
- return true;
- } else {
- // If there is a screen-on command in the notification queue, we
- // can't turn the screen on until it has been processed (and we
- // have set mPreparingForScreenOn) or it has been dropped.
- for (int i=0; i<mBroadcastQueue.length; i++) {
- if (mBroadcastQueue[i] == 1) {
- if (DEBUG_SCREEN_ON) Slog.i(TAG,
- "updateLights: delaying screen on due to notification queue");
- return true;
- }
- }
- }
- return false;
- }
-
- private void updateLightsLocked(int newState, int forceState) {
- final int oldState = mPowerState;
-
- // If the screen is not currently on, we will want to delay actually
- // turning the lights on if we are still getting the UI put up.
- if ((oldState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn) {
- // Don't turn screen on until we know we are really ready to.
- // This is to avoid letting the screen go on before things like the
- // lock screen have been displayed.
- if ((mSkippedScreenOn = shouldDeferScreenOnLocked())) {
- newState &= ~(SCREEN_ON_BIT|SCREEN_BRIGHT_BIT);
- }
- }
-
- if ((newState & SCREEN_ON_BIT) != 0) {
- // Only turn on the buttons or keyboard if the screen is also on.
- // We should never see the buttons on but not the screen.
- newState = applyButtonState(newState);
- newState = applyKeyboardState(newState);
- }
- final int realDifference = (newState ^ oldState);
- final int difference = realDifference | forceState;
- if (difference == 0) {
- return;
- }
-
- int offMask = 0;
- int dimMask = 0;
- int onMask = 0;
-
- int preferredBrightness = getPreferredBrightness();
-
- if ((difference & KEYBOARD_BRIGHT_BIT) != 0) {
- if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
- offMask |= KEYBOARD_BRIGHT_BIT;
- } else {
- onMask |= KEYBOARD_BRIGHT_BIT;
- }
- }
-
- if ((difference & BUTTON_BRIGHT_BIT) != 0) {
- if ((newState & BUTTON_BRIGHT_BIT) == 0) {
- offMask |= BUTTON_BRIGHT_BIT;
- } else {
- onMask |= BUTTON_BRIGHT_BIT;
- }
- }
-
- if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
- int nominalCurrentValue = -1;
- // If there was an actual difference in the light state, then
- // figure out the "ideal" current value based on the previous
- // state. Otherwise, this is a change due to the brightness
- // override, so we want to animate from whatever the current
- // value is.
- if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
- switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) {
- case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT:
- nominalCurrentValue = preferredBrightness;
- break;
- case SCREEN_ON_BIT:
- nominalCurrentValue = mScreenBrightnessDim;
- break;
- case 0:
- nominalCurrentValue = PowerManager.BRIGHTNESS_OFF;
- break;
- case SCREEN_BRIGHT_BIT:
- default:
- // not possible
- nominalCurrentValue = (int)mScreenBrightnessAnimator.getCurrentBrightness();
- break;
- }
- }
- int brightness = preferredBrightness;
- int steps = ANIM_STEPS;
- if ((newState & SCREEN_BRIGHT_BIT) == 0) {
- // dim or turn off backlight, depending on if the screen is on
- // the scale is because the brightness ramp isn't linear and this biases
- // it so the later parts take longer.
- final float scale = 1.5f;
- float ratio = (((float)mScreenBrightnessDim)/preferredBrightness);
- if (ratio > 1.0f) ratio = 1.0f;
- if ((newState & SCREEN_ON_BIT) == 0) {
- if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
- // was bright
- steps = ANIM_STEPS;
- } else {
- // was dim
- steps = (int)(ANIM_STEPS*ratio*scale);
- }
- brightness = PowerManager.BRIGHTNESS_OFF;
- } else {
- if ((oldState & SCREEN_ON_BIT) != 0) {
- // was bright
- steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
- } else {
- // was dim
- steps = (int)(ANIM_STEPS*ratio);
- }
- final int stayOnConditions = getStayOnConditionsLocked();
- if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) {
- // If the "stay on while plugged in" option is
- // turned on, then the screen will often not
- // automatically turn off while plugged in. To
- // still have a sense of when it is inactive, we
- // will then count going dim as turning off.
- mScreenOffTime = SystemClock.elapsedRealtime();
- }
- brightness = mScreenBrightnessDim;
- }
- }
- if (mWaitingForFirstLightSensor && (newState & SCREEN_ON_BIT) != 0) {
- steps = IMMEDIATE_ANIM_STEPS;
- }
-
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(brightness);
- } catch (RemoteException e) {
- // Nothing interesting to do.
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- if (!mSkippedScreenOn) {
- int dt = steps * NOMINAL_FRAME_TIME_MS;
- mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, dt);
- if (DEBUG_SCREEN_ON) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.i(TAG, "Setting screen brightness: " + brightness, e);
- }
- }
- }
-
- if (mSpew) {
- Slog.d(TAG, "offMask=0x" + Integer.toHexString(offMask)
- + " dimMask=0x" + Integer.toHexString(dimMask)
- + " onMask=0x" + Integer.toHexString(onMask)
- + " difference=0x" + Integer.toHexString(difference)
- + " realDifference=0x" + Integer.toHexString(realDifference)
- + " forceState=0x" + Integer.toHexString(forceState)
- );
- }
-
- if (offMask != 0) {
- if (mSpew) Slog.i(TAG, "Setting brightess off: " + offMask);
- setLightBrightness(offMask, PowerManager.BRIGHTNESS_OFF);
- }
- if (dimMask != 0) {
- int brightness = mScreenBrightnessDim;
- if ((newState & BATTERY_LOW_BIT) != 0 &&
- brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) {
- brightness = PowerManager.BRIGHTNESS_LOW_BATTERY;
- }
- if (mSpew) Slog.i(TAG, "Setting brightess dim " + brightness + ": " + dimMask);
- setLightBrightness(dimMask, brightness);
- }
- if (onMask != 0) {
- int brightness = getPreferredBrightness();
- if ((newState & BATTERY_LOW_BIT) != 0 &&
- brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) {
- brightness = PowerManager.BRIGHTNESS_LOW_BATTERY;
- }
- if (mSpew) Slog.i(TAG, "Setting brightess on " + brightness + ": " + onMask);
- setLightBrightness(onMask, brightness);
- }
- }
-
- /**
- * Note: by design this class does not hold mLocks while calling native methods.
- * Nor should it. Ever.
- */
- class ScreenBrightnessAnimator extends HandlerThread {
- static final int ANIMATE_LIGHTS = 10;
- static final int ANIMATE_POWER_OFF = 11;
- volatile int startValue;
- volatile int endValue;
- volatile int startSensorValue;
- volatile int endSensorValue;
- volatile int currentValue;
- private int currentMask;
- private int duration;
- private long startTimeMillis;
- private final String prefix;
-
- public ScreenBrightnessAnimator(String name, int priority) {
- super(name, priority);
- prefix = name;
- }
-
- @Override
- protected void onLooperPrepared() {
- mScreenBrightnessHandler = new Handler() {
- public void handleMessage(Message msg) {
- int brightnessMode = (mAutoBrightessEnabled && !mInitialAnimation
- ? LightsService.BRIGHTNESS_MODE_SENSOR
- : LightsService.BRIGHTNESS_MODE_USER);
- if (msg.what == ANIMATE_LIGHTS) {
- final int mask = msg.arg1;
- int value = msg.arg2;
- long tStart = SystemClock.uptimeMillis();
- if ((mask & SCREEN_BRIGHT_BIT) != 0) {
- if (mDebugLightAnimation) Slog.v(TAG, "Set brightness: " + value);
- mLcdLight.setBrightness(value, brightnessMode);
- }
- long elapsed = SystemClock.uptimeMillis() - tStart;
- if ((mask & BUTTON_BRIGHT_BIT) != 0) {
- mButtonLight.setBrightness(value);
- }
- if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
- mKeyboardLight.setBrightness(value);
- }
-
- if (elapsed > 100) {
- Slog.e(TAG, "Excessive delay setting brightness: " + elapsed
- + "ms, mask=" + mask);
- }
-
- // Throttle brightness updates to frame refresh rate
- int delay = elapsed < NOMINAL_FRAME_TIME_MS ? NOMINAL_FRAME_TIME_MS : 1;
- synchronized(this) {
- currentValue = value;
- }
- animateInternal(mask, false, delay);
- } else if (msg.what == ANIMATE_POWER_OFF) {
- int mode = msg.arg1;
- nativeStartSurfaceFlingerAnimation(mode);
- }
- }
- };
- synchronized (this) {
- mInitComplete = true;
- notifyAll();
- }
- }
-
- private void animateInternal(int mask, boolean turningOff, int delay) {
- synchronized (this) {
- if (currentValue != endValue) {
- final long now = SystemClock.elapsedRealtime();
- final int elapsed = (int) (now - startTimeMillis);
- int newValue;
- if (elapsed < duration) {
- int delta = endValue - startValue;
- newValue = startValue + delta * elapsed / duration;
- newValue = Math.max(PowerManager.BRIGHTNESS_OFF, newValue);
- newValue = Math.min(PowerManager.BRIGHTNESS_ON, newValue);
- // Optimization to delay next step until a change will occur.
- if (delay > 0 && newValue == currentValue) {
- final int timePerStep = duration / Math.abs(delta);
- delay = Math.min(duration - elapsed, timePerStep);
- newValue += delta < 0 ? -1 : 1;
- }
- // adjust the peak sensor value until we get to the target sensor value
- delta = endSensorValue - startSensorValue;
- mHighestLightSensorValue = startSensorValue + delta * elapsed / duration;
- } else {
- newValue = endValue;
- mHighestLightSensorValue = endSensorValue;
- if (endValue > 0) {
- mInitialAnimation = false;
- }
- }
-
- if (mDebugLightAnimation) {
- Slog.v(TAG, "Animating light: " + "start:" + startValue
- + ", end:" + endValue + ", elapsed:" + elapsed
- + ", duration:" + duration + ", current:" + currentValue
- + ", newValue:" + newValue
- + ", delay:" + delay
- + ", highestSensor:" + mHighestLightSensorValue);
- }
-
- if (turningOff && !mHeadless && !mAnimateScreenLights) {
- int mode = mScreenOffReason == OFF_BECAUSE_OF_PROX_SENSOR
- ? 0 : mAnimationSetting;
- if (mDebugLightAnimation) {
- Slog.v(TAG, "Doing power-off anim, mode=" + mode);
- }
- mScreenBrightnessHandler.obtainMessage(ANIMATE_POWER_OFF, mode, 0)
- .sendToTarget();
- }
- mScreenBrightnessHandler.removeMessages(
- ScreenBrightnessAnimator.ANIMATE_LIGHTS);
- Message msg = mScreenBrightnessHandler
- .obtainMessage(ANIMATE_LIGHTS, mask, newValue);
- mScreenBrightnessHandler.sendMessageDelayed(msg, delay);
- }
- }
- }
-
- public void dump(PrintWriter pw, String string) {
- pw.println(string);
- pw.println(" animating: " + "start:" + startValue + ", end:" + endValue
- + ", duration:" + duration + ", current:" + currentValue);
- pw.println(" startSensorValue:" + startSensorValue
- + " endSensorValue:" + endSensorValue);
- pw.println(" startTimeMillis:" + startTimeMillis
- + " now:" + SystemClock.elapsedRealtime());
- pw.println(" currentMask:" + dumpPowerState(currentMask));
- }
-
- public void animateTo(int target, int mask, int animationDuration) {
- animateTo(target, mHighestLightSensorValue, mask, animationDuration);
- }
-
- public void animateTo(int target, int sensorTarget, int mask, int animationDuration) {
- synchronized(this) {
- if ((mask & SCREEN_BRIGHT_BIT) == 0) {
- // We only animate keyboard and button when passed in with SCREEN_BRIGHT_BIT.
- if ((mask & BUTTON_BRIGHT_BIT) != 0) {
- mButtonLight.setBrightness(target);
- }
- if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
- mKeyboardLight.setBrightness(target);
- }
- return;
- }
- if (isAnimating() && (mask ^ currentMask) != 0) {
- // current animation is unrelated to new animation, jump to final values
- cancelAnimation();
- }
- if (mInitialAnimation) {
- // jump to final value in one step the first time the brightness is set
- animationDuration = 0;
- if (target > 0) {
- mInitialAnimation = false;
- }
- }
- startValue = currentValue;
- endValue = target;
- startSensorValue = mHighestLightSensorValue;
- endSensorValue = sensorTarget;
- currentMask = mask;
- duration = (int) (mWindowScaleAnimation * animationDuration);
- startTimeMillis = SystemClock.elapsedRealtime();
-
- if (mDebugLightAnimation) {
- Slog.v(TAG, "animateTo(target=" + target
- + ", sensor=" + sensorTarget
- + ", mask=" + mask
- + ", duration=" + animationDuration +")"
- + ", currentValue=" + currentValue
- + ", startTime=" + startTimeMillis);
- }
-
- if (target != currentValue) {
- final boolean doScreenAnim = (mask & (SCREEN_BRIGHT_BIT | SCREEN_ON_BIT)) != 0;
- final boolean turningOff = endValue == PowerManager.BRIGHTNESS_OFF;
- if (turningOff && doScreenAnim) {
- // Cancel all pending animations since we're turning off
- mScreenBrightnessHandler.removeCallbacksAndMessages(null);
- screenOffFinishedAnimatingLocked(mScreenOffReason);
- duration = 200; // TODO: how long should this be?
- }
- if (doScreenAnim) {
- animateInternal(mask, turningOff, 0);
- }
- // TODO: Handle keyboard light animation when we have devices that support it
- }
- }
- }
-
- public int getCurrentBrightness() {
- synchronized (this) {
- return currentValue;
- }
- }
-
- public boolean isAnimating() {
- synchronized (this) {
- return currentValue != endValue;
- }
- }
-
- public void cancelAnimation() {
- animateTo(endValue, currentMask, 0);
- }
- }
-
- private void setLightBrightness(int mask, int value) {
- mScreenBrightnessAnimator.animateTo(value, mask, 0);
- }
-
- private int getPreferredBrightness() {
- if (mScreenBrightnessOverride >= 0) {
- return mScreenBrightnessOverride;
- } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness
- && mAutoBrightessEnabled) {
- return mLightSensorScreenBrightness;
- }
- final int brightness = mScreenBrightnessSetting;
- // Don't let applications turn the screen all the way off
- return Math.max(brightness, mScreenBrightnessDim);
- }
-
- private int applyButtonState(int state) {
- int brightness = -1;
- if ((state & BATTERY_LOW_BIT) != 0) {
- // do not override brightness if the battery is low
- return state;
- }
- if (mButtonBrightnessOverride >= 0) {
- brightness = mButtonBrightnessOverride;
- } else if (mLightSensorButtonBrightness >= 0 && mUseSoftwareAutoBrightness) {
- brightness = mLightSensorButtonBrightness;
- }
- if (brightness > 0) {
- return state | BUTTON_BRIGHT_BIT;
- } else if (brightness == 0) {
- return state & ~BUTTON_BRIGHT_BIT;
- } else {
- return state;
- }
- }
-
- private int applyKeyboardState(int state) {
- int brightness = -1;
- if ((state & BATTERY_LOW_BIT) != 0) {
- // do not override brightness if the battery is low
- return state;
- }
- if (!mKeyboardVisible) {
- brightness = 0;
- } else if (mButtonBrightnessOverride >= 0) {
- brightness = mButtonBrightnessOverride;
- } else if (mLightSensorKeyboardBrightness >= 0 && mUseSoftwareAutoBrightness) {
- brightness = mLightSensorKeyboardBrightness;
- }
- if (brightness > 0) {
- return state | KEYBOARD_BRIGHT_BIT;
- } else if (brightness == 0) {
- return state & ~KEYBOARD_BRIGHT_BIT;
- } else {
- return state;
- }
- }
-
- public boolean isScreenOn() {
- synchronized (mLocks) {
- return (mPowerState & SCREEN_ON_BIT) != 0;
- }
- }
-
- boolean isScreenBright() {
- synchronized (mLocks) {
- return (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT;
- }
- }
-
- private boolean isScreenTurningOffLocked() {
- return (mScreenBrightnessAnimator.isAnimating()
- && mScreenBrightnessAnimator.endValue == PowerManager.BRIGHTNESS_OFF
- && (mScreenBrightnessAnimator.currentMask & SCREEN_BRIGHT_BIT) != 0);
- }
-
- private boolean shouldLog(long time) {
- synchronized (mLocks) {
- if (time > (mWarningSpewThrottleTime + (60*60*1000))) {
- mWarningSpewThrottleTime = time;
- mWarningSpewThrottleCount = 0;
- return true;
- } else if (mWarningSpewThrottleCount < 30) {
- mWarningSpewThrottleCount++;
- return true;
- } else {
- return false;
- }
- }
- }
-
- private void forceUserActivityLocked() {
- if (isScreenTurningOffLocked()) {
- // cancel animation so userActivity will succeed
- mScreenBrightnessAnimator.cancelAnimation();
- }
- boolean savedActivityAllowed = mUserActivityAllowed;
- mUserActivityAllowed = true;
- userActivity(SystemClock.uptimeMillis(), false);
- mUserActivityAllowed = savedActivityAllowed;
- }
-
- public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- userActivity(time, -1, noChangeLights, OTHER_EVENT, force, false);
- }
-
- public void userActivity(long time, boolean noChangeLights) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
- != PackageManager.PERMISSION_GRANTED) {
- if (shouldLog(time)) {
- Slog.w(TAG, "Caller does not have DEVICE_POWER permission. pid="
- + Binder.getCallingPid() + " uid=" + Binder.getCallingUid());
- }
- return;
- }
-
- userActivity(time, -1, noChangeLights, OTHER_EVENT, false, false);
- }
-
- public void userActivity(long time, boolean noChangeLights, int eventType) {
- userActivity(time, -1, noChangeLights, eventType, false, false);
- }
-
- public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) {
- userActivity(time, -1, noChangeLights, eventType, force, false);
- }
-
- /*
- * Reset the user activity timeout to now + timeout. This overrides whatever else is going
- * on with user activity. Don't use this function.
- */
- public void clearUserActivityTimeout(long now, long timeout) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now");
- userActivity(now, timeout, false, OTHER_EVENT, false, false);
- }
-
- private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
- int eventType, boolean force, boolean ignoreIfScreenOff) {
-
- if (((mPokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0) && (eventType == TOUCH_EVENT)) {
- if (false) {
- Slog.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey));
- }
- return;
- }
-
- synchronized (mLocks) {
- if (mSpew) {
- Slog.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time
- + " mUserActivityAllowed=" + mUserActivityAllowed
- + " mUserState=0x" + Integer.toHexString(mUserState)
- + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
- + " mProximitySensorActive=" + mProximitySensorActive
- + " timeoutOverride=" + timeoutOverride
- + " force=" + force);
- }
- // ignore user activity if we are in the process of turning off the screen
- if (isScreenTurningOffLocked()) {
- Slog.d(TAG, "ignoring user activity while turning off screen");
- return;
- }
- // ignore if the caller doesn't want this to allow the screen to turn
- // on, and the screen is currently off.
- if (ignoreIfScreenOff && (mPowerState & SCREEN_ON_BIT) == 0) {
- return;
- }
- // Disable proximity sensor if if user presses power key while we are in the
- // "waiting for proximity sensor to go negative" state.
- if (mProximitySensorActive && mProximityWakeLockCount == 0) {
- mProximitySensorActive = false;
- }
- if (mLastEventTime <= time || force) {
- mLastEventTime = time;
- if ((mUserActivityAllowed && !mProximitySensorActive) || force) {
- // Only turn on button backlights if a button was pressed
- // and auto brightness is disabled
- if (eventType == BUTTON_EVENT && !mUseSoftwareAutoBrightness) {
- mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
- } else {
- // don't clear button/keyboard backlights when the screen is touched.
- mUserState |= SCREEN_BRIGHT;
- }
-
- int uid = Binder.getCallingUid();
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteUserActivity(uid, eventType);
- } catch (RemoteException e) {
- // Ignore
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- mWakeLockState = mLocks.reactivateScreenLocksLocked();
- setPowerState(mUserState | mWakeLockState, noChangeLights,
- WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
- }
- }
- }
-
- if (mPolicy != null) {
- mPolicy.userActivity();
- }
- }
-
- private int getAutoBrightnessValue(int sensorValue, int[] values) {
- try {
- int i;
- for (i = 0; i < mAutoBrightnessLevels.length; i++) {
- if (sensorValue < mAutoBrightnessLevels[i]) {
- break;
- }
- }
- // This is the range of brightness values that we can use.
- final int minval = values[0];
- final int maxval = values[mAutoBrightnessLevels.length];
- // This is the range we will be scaling. We put some padding
- // at the low and high end to give the adjustment a little better
- // impact on the actual observed value.
- final int range = (maxval-minval) + LIGHT_SENSOR_RANGE_EXPANSION;
- // This is the desired brightness value from 0.0 to 1.0.
- float valf = ((values[i]-minval+(LIGHT_SENSOR_RANGE_EXPANSION/2))/(float)range);
- // Apply a scaling to the value based on the adjustment.
- if (mLightSensorAdjustSetting > 0 && mLightSensorAdjustSetting <= 1) {
- float adj = (float)Math.sqrt(1.0f-mLightSensorAdjustSetting);
- if (adj <= .00001) {
- valf = 1;
- } else {
- valf /= adj;
- }
- } else if (mLightSensorAdjustSetting < 0 && mLightSensorAdjustSetting >= -1) {
- float adj = (float)Math.sqrt(1.0f+mLightSensorAdjustSetting);
- valf *= adj;
- }
- // Apply an additional offset to the value based on the adjustment.
- valf += mLightSensorAdjustSetting/LIGHT_SENSOR_OFFSET_SCALE;
- // Convert the 0.0-1.0 value back to a brightness integer.
- int val = (int)((valf*range)+minval) - (LIGHT_SENSOR_RANGE_EXPANSION/2);
- if (val < minval) val = minval;
- else if (val > maxval) val = maxval;
- return val;
- } catch (Exception e) {
- // guard against null pointer or index out of bounds errors
- Slog.e(TAG, "Values array must be non-empty and must be one element longer than "
- + "the auto-brightness levels array. Check config.xml.", e);
- return 255;
- }
- }
-
- private Runnable mProximityTask = new Runnable() {
- public void run() {
- synchronized (mLocks) {
- if (mProximityPendingValue != -1) {
- proximityChangedLocked(mProximityPendingValue == 1);
- mProximityPendingValue = -1;
- }
- if (mProximityPartialLock.isHeld()) {
- mProximityPartialLock.release();
- }
- }
- }
- };
-
- private Runnable mAutoBrightnessTask = new Runnable() {
- public void run() {
- synchronized (mLocks) {
- if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
- int value = (int)mLightSensorPendingValue;
- mLightSensorPendingDecrease = false;
- mLightSensorPendingIncrease = false;
- lightSensorChangedLocked(value, false);
- }
- }
- }
- };
-
- /** used to prevent lightsensor changes while turning on. */
- private boolean mInitialAnimation = true;
-
- private void dockStateChanged(int state) {
- synchronized (mLocks) {
- mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED);
- if (mIsDocked) {
- // allow brightness to decrease when docked
- mHighestLightSensorValue = -1;
- }
- if ((mPowerState & SCREEN_ON_BIT) != 0) {
- // force lights recalculation
- int value = (int)mLightSensorValue;
- mLightSensorValue = -1;
- lightSensorChangedLocked(value, false);
- }
- }
- }
-
- private void lightSensorChangedLocked(int value, boolean immediate) {
- if (mDebugLightSensor) {
- Slog.d(TAG, "lightSensorChangedLocked value=" + value + " immediate=" + immediate);
- }
-
- // Don't do anything if the screen is off.
- if ((mPowerState & SCREEN_ON_BIT) == 0) {
- if (mDebugLightSensor) {
- Slog.d(TAG, "dropping lightSensorChangedLocked because screen is off");
- }
- return;
- }
-
- if (mLightSensorValue != value) {
- mLightSensorValue = value;
- if ((mPowerState & BATTERY_LOW_BIT) == 0) {
- // use maximum light sensor value seen since screen went on for LCD to avoid flicker
- // we only do this if we are undocked, since lighting should be stable when
- // stationary in a dock.
- int lcdValue = getAutoBrightnessValue(value, mLcdBacklightValues);
- int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues);
- int keyboardValue;
- if (mKeyboardVisible) {
- keyboardValue = getAutoBrightnessValue(value, mKeyboardBacklightValues);
- } else {
- keyboardValue = 0;
- }
- mLightSensorScreenBrightness = lcdValue;
- mLightSensorButtonBrightness = buttonValue;
- mLightSensorKeyboardBrightness = keyboardValue;
-
- if (mDebugLightSensor) {
- Slog.d(TAG, "lcdValue " + lcdValue);
- Slog.d(TAG, "buttonValue " + buttonValue);
- Slog.d(TAG, "keyboardValue " + keyboardValue);
- }
-
- if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) {
- if (!mSkippedScreenOn && !mInitialAnimation) {
- final int steps;
- if (immediate) {
- steps = IMMEDIATE_ANIM_STEPS;
- } else {
- synchronized (mScreenBrightnessAnimator) {
- if (mScreenBrightnessAnimator.currentValue <= lcdValue) {
- steps = AUTOBRIGHTNESS_ANIM_STEPS;
- } else {
- steps = AUTODIMNESS_ANIM_STEPS;
- }
- }
- }
- mScreenBrightnessAnimator.animateTo(lcdValue, value,
- SCREEN_BRIGHT_BIT, steps * NOMINAL_FRAME_TIME_MS);
- }
- }
- if (mButtonBrightnessOverride < 0) {
- mButtonLight.setBrightness(buttonValue);
- }
- if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) {
- mKeyboardLight.setBrightness(keyboardValue);
- }
- }
- }
- }
-
- /**
- * The user requested that we go to sleep (probably with the power button).
- * This overrides all wake locks that are held.
- */
- public void goToSleep(long time)
- {
- goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- }
-
- /**
- * The user requested that we go to sleep (probably with the power button).
- * This overrides all wake locks that are held.
- */
- public void goToSleepWithReason(long time, int reason)
- {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- synchronized (mLocks) {
- goToSleepLocked(time, reason);
- }
- }
-
- /**
- * Reboot the device immediately, passing 'reason' (may be null)
- * to the underlying __reboot system call. Should not return.
- */
- public void reboot(String reason)
- {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
-
- if (mHandler == null || !ActivityManagerNative.isSystemReady()) {
- throw new IllegalStateException("Too early to call reboot()");
- }
-
- final String finalReason = reason;
- Runnable runnable = new Runnable() {
- public void run() {
- synchronized (this) {
- ShutdownThread.reboot(mContext, finalReason, false);
- }
-
- }
- };
- // ShutdownThread must run on a looper capable of displaying the UI.
- mHandler.post(runnable);
-
- // PowerManager.reboot() is documented not to return so just wait for the inevitable.
- synchronized (runnable) {
- while (true) {
- try {
- runnable.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- }
-
- /**
- * Crash the runtime (causing a complete restart of the Android framework).
- * Requires REBOOT permission. Mostly for testing. Should not return.
- */
- public void crash(final String message)
- {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
- Thread t = new Thread("PowerManagerService.crash()") {
- public void run() { throw new RuntimeException(message); }
- };
- try {
- t.start();
- t.join();
- } catch (InterruptedException e) {
- Log.wtf(TAG, e);
- }
- }
-
- private void goToSleepLocked(long time, int reason) {
- if (mSpew) {
- Exception ex = new Exception();
- ex.fillInStackTrace();
- Slog.d(TAG, "goToSleep mLastEventTime=" + mLastEventTime + " time=" + time
- + " reason=" + reason, ex);
- }
-
- if (mLastEventTime <= time) {
- mLastEventTime = time;
- // cancel all of the wake locks
- mWakeLockState = SCREEN_OFF;
- int N = mLocks.size();
- int numCleared = 0;
- boolean proxLock = false;
- for (int i=0; i<N; i++) {
- WakeLock wl = mLocks.get(i);
- if (isScreenLock(wl.flags)) {
- if (((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
- && reason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
- proxLock = true;
- } else {
- mLocks.get(i).activated = false;
- numCleared++;
- }
- }
- }
- if (!proxLock) {
- mProxIgnoredBecauseScreenTurnedOff = true;
- if (mDebugProximitySensor) {
- Slog.d(TAG, "setting mProxIgnoredBecauseScreenTurnedOff");
- }
- }
- EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numCleared);
- mStillNeedSleepNotification = true;
- mUserState = SCREEN_OFF;
- setPowerState(SCREEN_OFF, false, reason);
- cancelTimerLocked();
- }
- }
-
- public long timeSinceScreenOn() {
- synchronized (mLocks) {
- if ((mPowerState & SCREEN_ON_BIT) != 0) {
- return 0;
- }
- return SystemClock.elapsedRealtime() - mScreenOffTime;
- }
- }
-
- public void setKeyboardVisibility(boolean visible) {
- synchronized (mLocks) {
- if (mSpew) {
- Slog.d(TAG, "setKeyboardVisibility: " + visible);
- }
- if (mKeyboardVisible != visible) {
- mKeyboardVisible = visible;
- // don't signal user activity if the screen is off; other code
- // will take care of turning on due to a true change to the lid
- // switch and synchronized with the lock screen.
- if ((mPowerState & SCREEN_ON_BIT) != 0) {
- if (mUseSoftwareAutoBrightness) {
- // force recompute of backlight values
- if (mLightSensorValue >= 0) {
- int value = (int)mLightSensorValue;
- mLightSensorValue = -1;
- lightSensorChangedLocked(value, false);
- }
- }
- userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
- }
- }
- }
- }
-
- /**
- * When the keyguard is up, it manages the power state, and userActivity doesn't do anything.
- * When disabling user activity we also reset user power state so the keyguard can reset its
- * short screen timeout when keyguard is unhidden.
- */
- public void enableUserActivity(boolean enabled) {
- if (mSpew) {
- Slog.d(TAG, "enableUserActivity " + enabled);
- }
- synchronized (mLocks) {
- mUserActivityAllowed = enabled;
- if (!enabled) {
- // cancel timeout and clear mUserState so the keyguard can set a short timeout
- setTimeoutLocked(SystemClock.uptimeMillis(), 0);
- }
- }
- }
-
- private void setScreenBrightnessMode(int mode) {
- synchronized (mLocks) {
- boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
- if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) {
- mAutoBrightessEnabled = enabled;
- // This will get us a new value
- enableLightSensorLocked(mAutoBrightessEnabled && isScreenOn());
- }
- }
- }
-
- /** Sets the screen off timeouts:
- * mKeylightDelay
- * mDimDelay
- * mScreenOffDelay
- * */
- private void setScreenOffTimeoutsLocked() {
- if ((mPokey & POKE_LOCK_SHORT_TIMEOUT) != 0) {
- mKeylightDelay = mShortKeylightDelay; // Configurable via secure settings
- mDimDelay = -1;
- mScreenOffDelay = 0;
- } else if ((mPokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0) {
- mKeylightDelay = MEDIUM_KEYLIGHT_DELAY;
- mDimDelay = -1;
- mScreenOffDelay = 0;
- } else {
- int totalDelay = mScreenOffTimeoutSetting;
- if (totalDelay > mMaximumScreenOffTimeout) {
- totalDelay = mMaximumScreenOffTimeout;
- }
- mKeylightDelay = LONG_KEYLIGHT_DELAY;
- if (totalDelay < 0) {
- // negative number means stay on as long as possible.
- mScreenOffDelay = mMaximumScreenOffTimeout;
- } else if (mKeylightDelay < totalDelay) {
- // subtract the time that the keylight delay. This will give us the
- // remainder of the time that we need to sleep to get the accurate
- // screen off timeout.
- mScreenOffDelay = totalDelay - mKeylightDelay;
- } else {
- mScreenOffDelay = 0;
- }
- if (mDimScreen && totalDelay >= (LONG_KEYLIGHT_DELAY + LONG_DIM_TIME)) {
- mDimDelay = mScreenOffDelay - LONG_DIM_TIME;
- mScreenOffDelay = LONG_DIM_TIME;
- } else {
- mDimDelay = -1;
- }
- }
- if (mSpew) {
- Slog.d(TAG, "setScreenOffTimeouts mKeylightDelay=" + mKeylightDelay
- + " mDimDelay=" + mDimDelay + " mScreenOffDelay=" + mScreenOffDelay
- + " mDimScreen=" + mDimScreen);
- }
- }
-
- /**
- * Refreshes cached secure settings. Called once on startup, and
- * on subsequent changes to secure settings.
- */
- private void updateSettingsValues() {
- mShortKeylightDelay = Settings.Secure.getInt(
- mContext.getContentResolver(),
- Settings.Secure.SHORT_KEYLIGHT_DELAY_MS,
- SHORT_KEYLIGHT_DELAY_DEFAULT);
- // Slog.i(TAG, "updateSettingsValues(): mShortKeylightDelay now " + mShortKeylightDelay);
- }
-
- private class LockList extends ArrayList<WakeLock>
- {
- void addLock(WakeLock wl)
- {
- int index = getIndex(wl.binder);
- if (index < 0) {
- this.add(wl);
- }
- }
-
- WakeLock removeLock(IBinder binder)
- {
- int index = getIndex(binder);
- if (index >= 0) {
- return this.remove(index);
- } else {
- return null;
- }
- }
-
- int getIndex(IBinder binder)
- {
- int N = this.size();
- for (int i=0; i<N; i++) {
- if (this.get(i).binder == binder) {
- return i;
- }
- }
- return -1;
- }
-
- int gatherState()
- {
- int result = 0;
- int N = this.size();
- for (int i=0; i<N; i++) {
- WakeLock wl = this.get(i);
- if (wl.activated) {
- if (isScreenLock(wl.flags)) {
- result |= wl.minState;
- }
- }
- }
- return result;
- }
-
- int reactivateScreenLocksLocked()
- {
- int result = 0;
- int N = this.size();
- for (int i=0; i<N; i++) {
- WakeLock wl = this.get(i);
- if (isScreenLock(wl.flags)) {
- wl.activated = true;
- result |= wl.minState;
- }
- }
- if (mDebugProximitySensor) {
- Slog.d(TAG, "reactivateScreenLocksLocked mProxIgnoredBecauseScreenTurnedOff="
- + mProxIgnoredBecauseScreenTurnedOff);
- }
- mProxIgnoredBecauseScreenTurnedOff = false;
- return result;
- }
- }
-
- public void setPolicy(WindowManagerPolicy p) {
- synchronized (mLocks) {
- mPolicy = p;
- mLocks.notifyAll();
- }
- }
-
- WindowManagerPolicy getPolicyLocked() {
- while (mPolicy == null || !mDoneBooting) {
- try {
- mLocks.wait();
- } catch (InterruptedException e) {
- // Ignore
- }
- }
- return mPolicy;
- }
-
- void systemReady() {
- mSensorManager = new SystemSensorManager(mHandlerThread.getLooper());
- mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- // don't bother with the light sensor if auto brightness is handled in hardware
- if (mUseSoftwareAutoBrightness) {
- mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
- }
-
- // wait until sensors are enabled before turning on screen.
- // some devices will not activate the light sensor properly on boot
- // unless we do this.
- if (mUseSoftwareAutoBrightness) {
- // turn the screen on
- setPowerState(SCREEN_BRIGHT);
- } else {
- // turn everything on
- setPowerState(ALL_BRIGHT);
- }
-
- synchronized (mLocks) {
- Slog.d(TAG, "system ready!");
- mDoneBooting = true;
-
- enableLightSensorLocked(mUseSoftwareAutoBrightness && mAutoBrightessEnabled);
-
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(getPreferredBrightness());
- mBatteryStats.noteScreenOn();
- } catch (RemoteException e) {
- // Nothing interesting to do.
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- void bootCompleted() {
- Slog.d(TAG, "bootCompleted");
- synchronized (mLocks) {
- mBootCompleted = true;
- userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
- updateWakeLockLocked();
- mLocks.notifyAll();
- }
- }
-
- // for watchdog
- public void monitor() {
- synchronized (mLocks) { }
- }
-
- public int getSupportedWakeLockFlags() {
- int result = PowerManager.PARTIAL_WAKE_LOCK
- | PowerManager.FULL_WAKE_LOCK
- | PowerManager.SCREEN_DIM_WAKE_LOCK;
-
- if (mProximitySensor != null) {
- result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
- }
-
- return result;
- }
-
- public void setBacklightBrightness(int brightness) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- // Don't let applications turn the screen all the way off
- synchronized (mLocks) {
- brightness = Math.max(brightness, mScreenBrightnessDim);
- mLcdLight.setBrightness(brightness);
- mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0);
- mButtonLight.setBrightness(brightness);
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(brightness);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, 0);
- }
- }
-
- public void setAutoBrightnessAdjustment(float adj) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- synchronized (mLocks) {
- mLightSensorAdjustSetting = adj;
- if (mSensorManager != null && mLightSensorEnabled) {
- // clear calling identity so sensor manager battery stats are accurate
- long identity = Binder.clearCallingIdentity();
- try {
- // force recompute of backlight values
- if (mLightSensorValue >= 0) {
- int value = (int)mLightSensorValue;
- mLightSensorValue = -1;
- handleLightSensorValue(value, true);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
- }
-
- public void setAttentionLight(boolean on, int color) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- mAttentionLight.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
- }
-
- private void enableProximityLockLocked() {
- if (mDebugProximitySensor) {
- Slog.d(TAG, "enableProximityLockLocked");
- }
- if (!mProximitySensorEnabled) {
- // clear calling identity so sensor manager battery stats are accurate
- long identity = Binder.clearCallingIdentity();
- try {
- mSensorManager.registerListener(mProximityListener, mProximitySensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- mProximitySensorEnabled = true;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- private void disableProximityLockLocked() {
- if (mDebugProximitySensor) {
- Slog.d(TAG, "disableProximityLockLocked");
- }
- if (mProximitySensorEnabled) {
- // clear calling identity so sensor manager battery stats are accurate
- long identity = Binder.clearCallingIdentity();
- try {
- mSensorManager.unregisterListener(mProximityListener);
- mHandler.removeCallbacks(mProximityTask);
- if (mProximityPartialLock.isHeld()) {
- mProximityPartialLock.release();
- }
- mProximitySensorEnabled = false;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- if (mProximitySensorActive) {
- mProximitySensorActive = false;
- if (mDebugProximitySensor) {
- Slog.d(TAG, "disableProximityLockLocked mProxIgnoredBecauseScreenTurnedOff="
- + mProxIgnoredBecauseScreenTurnedOff);
- }
- if (!mProxIgnoredBecauseScreenTurnedOff) {
- forceUserActivityLocked();
- }
- }
- }
- }
-
- private void proximityChangedLocked(boolean active) {
- if (mDebugProximitySensor) {
- Slog.d(TAG, "proximityChangedLocked, active: " + active);
- }
- if (!mProximitySensorEnabled) {
- Slog.d(TAG, "Ignoring proximity change after sensor is disabled");
- return;
- }
- if (active) {
- if (mDebugProximitySensor) {
- Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff="
- + mProxIgnoredBecauseScreenTurnedOff);
- }
- if (!mProxIgnoredBecauseScreenTurnedOff) {
- goToSleepLocked(SystemClock.uptimeMillis(),
- WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR);
- }
- mProximitySensorActive = true;
- } else {
- // proximity sensor negative events trigger as user activity.
- // temporarily set mUserActivityAllowed to true so this will work
- // even when the keyguard is on.
- mProximitySensorActive = false;
- if (mDebugProximitySensor) {
- Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff="
- + mProxIgnoredBecauseScreenTurnedOff);
- }
- if (!mProxIgnoredBecauseScreenTurnedOff) {
- forceUserActivityLocked();
- }
-
- if (mProximityWakeLockCount == 0) {
- // disable sensor if we have no listeners left after proximity negative
- disableProximityLockLocked();
- }
- }
- }
-
- private void enableLightSensorLocked(boolean enable) {
- if (mDebugLightSensor) {
- Slog.d(TAG, "enableLightSensorLocked enable=" + enable
- + " mLightSensorEnabled=" + mLightSensorEnabled
- + " mAutoBrightessEnabled=" + mAutoBrightessEnabled
- + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor);
- }
- if (!mAutoBrightessEnabled) {
- enable = false;
- }
- if (mSensorManager != null && mLightSensorEnabled != enable) {
- mLightSensorEnabled = enable;
- // clear calling identity so sensor manager battery stats are accurate
- long identity = Binder.clearCallingIdentity();
- try {
- if (enable) {
- // reset our highest value when reenabling
- mHighestLightSensorValue = -1;
- // force recompute of backlight values
- final int value = (int)mLightSensorValue;
- if (value >= 0) {
- mLightSensorValue = -1;
- handleLightSensorValue(value, true);
- }
- mSensorManager.registerListener(mLightListener, mLightSensor,
- LIGHT_SENSOR_RATE);
- } else {
- mSensorManager.unregisterListener(mLightListener);
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = false;
- mLightSensorPendingIncrease = false;
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- SensorEventListener mProximityListener = new SensorEventListener() {
- public void onSensorChanged(SensorEvent event) {
- long milliseconds = SystemClock.elapsedRealtime();
- synchronized (mLocks) {
- float distance = event.values[0];
- long timeSinceLastEvent = milliseconds - mLastProximityEventTime;
- mLastProximityEventTime = milliseconds;
- mHandler.removeCallbacks(mProximityTask);
- boolean proximityTaskQueued = false;
-
- // compare against getMaximumRange to support sensors that only return 0 or 1
- boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&
- distance < mProximitySensor.getMaximumRange());
-
- if (mDebugProximitySensor) {
- Slog.d(TAG, "mProximityListener.onSensorChanged active: " + active);
- }
- if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) {
- // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing
- mProximityPendingValue = (active ? 1 : 0);
- mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent);
- proximityTaskQueued = true;
- } else {
- // process the value immediately
- mProximityPendingValue = -1;
- proximityChangedLocked(active);
- }
-
- // update mProximityPartialLock state
- boolean held = mProximityPartialLock.isHeld();
- if (!held && proximityTaskQueued) {
- // hold wakelock until mProximityTask runs
- mProximityPartialLock.acquire();
- } else if (held && !proximityTaskQueued) {
- mProximityPartialLock.release();
- }
- }
- }
-
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // ignore
- }
- };
-
- private void handleLightSensorValue(int value, boolean immediate) {
- long milliseconds = SystemClock.elapsedRealtime();
- if (mLightSensorValue == -1
- || milliseconds < mLastScreenOnTime + mLightSensorWarmupTime
- || mWaitingForFirstLightSensor) {
- // process the value immediately if screen has just turned on
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = false;
- mLightSensorPendingIncrease = false;
- lightSensorChangedLocked(value, immediate);
- } else {
- if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
- (value < mLightSensorValue && mLightSensorPendingIncrease) ||
- (value == mLightSensorValue) ||
- (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
- // delay processing to debounce the sensor
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = (value < mLightSensorValue);
- mLightSensorPendingIncrease = (value > mLightSensorValue);
- if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
- mLightSensorPendingValue = value;
- mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
- }
- } else {
- mLightSensorPendingValue = value;
- }
- }
- }
-
- SensorEventListener mLightListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mDebugLightSensor) {
- Slog.d(TAG, "onSensorChanged: light value: " + event.values[0]);
- }
- synchronized (mLocks) {
- // ignore light sensor while screen is turning off
- if (isScreenTurningOffLocked()) {
- return;
- }
- handleLightSensorValue((int)event.values[0], mWaitingForFirstLightSensor);
- if (mWaitingForFirstLightSensor && !mPreparingForScreenOn) {
- if (mDebugLightAnimation) {
- Slog.d(TAG, "onSensorChanged: Clearing mWaitingForFirstLightSensor.");
- }
- mWaitingForFirstLightSensor = false;
- }
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // ignore
- }
- };
-}
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
new file mode 100644
index 0000000..0dfaa05
--- /dev/null
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.content.PackageMonitor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Find the best Service, and bind to it.
+ * Handles run-time package changes.
+ */
+public class ServiceWatcher implements ServiceConnection {
+ private static final boolean D = false;
+ private static final String EXTRA_VERSION = "version";
+
+ private final String mTag;
+ private final Context mContext;
+ private final PackageManager mPm;
+ private final List<HashSet<Signature>> mSignatureSets;
+ private final String mAction;
+ private final Runnable mNewServiceWork;
+ private final Handler mHandler;
+
+ private Object mLock = new Object();
+
+ // all fields below synchronized on mLock
+ private IBinder mBinder; // connected service
+ private String mPackageName; // current best package
+ private int mVersion; // current best version
+
+ public ServiceWatcher(Context context, String logTag, String action,
+ List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
+ mContext = context;
+ mTag = logTag;
+ mAction = action;
+ mPm = mContext.getPackageManager();
+ mNewServiceWork = newServiceWork;
+ mHandler = handler;
+
+ mSignatureSets = new ArrayList<HashSet<Signature>>();
+ for (int i=0; i < initialPackageNames.size(); i++) {
+ String pkg = initialPackageNames.get(i);
+ HashSet<Signature> set = new HashSet<Signature>();
+ try {
+ Signature[] sigs =
+ mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
+ set.addAll(Arrays.asList(sigs));
+ mSignatureSets.add(set);
+ } catch (NameNotFoundException e) {
+ Log.w(logTag, pkg + " not found");
+ }
+ }
+
+ }
+
+ public boolean start() {
+ if (!bindBestPackage(null)) return false;
+
+ mPackageMonitor.register(mContext, null, true);
+ return true;
+ }
+
+ /**
+ * Searches and binds to the best package, or do nothing
+ * if the best package is already bound.
+ * Only checks the named package, or checks all packages if it
+ * is null.
+ * Return true if a new package was found to bind to.
+ */
+ private boolean bindBestPackage(String justCheckThisPackage) {
+ Intent intent = new Intent(mAction);
+ if (justCheckThisPackage != null) {
+ intent.setPackage(justCheckThisPackage);
+ }
+ List<ResolveInfo> rInfos = mPm.queryIntentServices(new Intent(mAction),
+ PackageManager.GET_META_DATA);
+ int bestVersion = Integer.MIN_VALUE;
+ String bestPackage = null;
+ for (ResolveInfo rInfo : rInfos) {
+ String packageName = rInfo.serviceInfo.packageName;
+
+ // check signature
+ try {
+ PackageInfo pInfo;
+ pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ if (!isSignatureMatch(pInfo.signatures)) {
+ Log.w(mTag, packageName + " resolves service " + mAction +
+ ", but has wrong signature, ignoring");
+ continue;
+ }
+ } catch (NameNotFoundException e) {
+ Log.wtf(mTag, e);
+ continue;
+ }
+
+ // check version
+ int version = 0;
+ if (rInfo.serviceInfo.metaData != null) {
+ version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0);
+ }
+ if (version > mVersion) {
+ bestVersion = version;
+ bestPackage = packageName;
+ }
+ }
+
+ if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s",
+ (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
+ rInfos.size(),
+ (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
+
+ if (bestPackage != null) {
+ bindToPackage(bestPackage, bestVersion);
+ return true;
+ }
+ return false;
+ }
+
+ private void unbind() {
+ String pkg;
+ synchronized (mLock) {
+ pkg = mPackageName;
+ mPackageName = null;
+ mVersion = Integer.MIN_VALUE;
+ }
+ if (pkg != null) {
+ if (D) Log.d(mTag, "unbinding " + pkg);
+ mContext.unbindService(this);
+ }
+ }
+
+ private void bindToPackage(String packageName, int version) {
+ unbind();
+ Intent intent = new Intent(mAction);
+ intent.setPackage(packageName);
+ synchronized (mLock) {
+ mPackageName = packageName;
+ mVersion = version;
+ }
+ if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
+ mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ }
+
+ private boolean isSignatureMatch(Signature[] signatures) {
+ if (signatures == null) return false;
+
+ // build hashset of input to test against
+ HashSet<Signature> inputSet = new HashSet<Signature>();
+ for (Signature s : signatures) {
+ inputSet.add(s);
+ }
+
+ // test input against each of the signature sets
+ for (HashSet<Signature> referenceSet : mSignatureSets) {
+ if (referenceSet.equals(inputSet)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ /**
+ * Called when package has been reinstalled
+ */
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbind();
+ }
+ // check the updated package in case it is better
+ bindBestPackage(packageName);
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbind();
+ }
+ // check the new package is case it is better
+ bindBestPackage(packageName);
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ unbind();
+ // the currently bound package was removed,
+ // need to search for a new package
+ bindBestPackage(null);
+ }
+ }
+ };
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ synchronized (mLock) {
+ String packageName = name.getPackageName();
+ if (packageName.equals(mPackageName)) {
+ if (D) Log.d(mTag, packageName + " connected");
+ mBinder = binder;
+ if (mHandler !=null && mNewServiceWork != null) {
+ mHandler.post(mNewServiceWork);
+ }
+ } else {
+ Log.w(mTag, "unexpected onServiceConnected: " + packageName);
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ String packageName = name.getPackageName();
+ if (D) Log.d(mTag, packageName + " disconnected");
+
+ if (packageName.equals(mPackageName)) {
+ mBinder = null;
+ }
+ }
+ }
+
+ public String getBestPackageName() {
+ synchronized (mLock) {
+ return mPackageName;
+ }
+ }
+
+ public int getBestVersion() {
+ synchronized (mLock) {
+ return mVersion;
+ }
+ }
+
+ public IBinder getBinder() {
+ synchronized (mLock) {
+ return mBinder;
+ }
+ }
+}
diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java
index d85abe6..a4341b7 100644
--- a/services/java/com/android/server/ShutdownActivity.java
+++ b/services/java/com/android/server/ShutdownActivity.java
@@ -17,13 +17,12 @@
package com.android.server;
import android.app.Activity;
-import android.content.BroadcastReceiver;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Slog;
-import com.android.server.pm.ShutdownThread;
+import com.android.server.power.ShutdownThread;
public class ShutdownActivity extends Activity {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e55e7fe..7097891 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,14 +36,13 @@ import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.server.BluetoothA2dpService;
-import android.server.BluetoothService;
import android.server.search.SearchManagerService;
import android.service.dreams.DreamManagerService;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.view.Display;
import android.view.WindowManager;
import com.android.internal.os.BinderInternal;
@@ -51,11 +50,15 @@ import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.widget.LockSettingsService;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.display.DisplayManagerService;
import com.android.server.input.InputManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.ShutdownThread;
+import com.android.server.pm.UserManagerService;
+import com.android.server.power.PowerManagerService;
+import com.android.server.power.ShutdownThread;
import com.android.server.usb.UsbService;
import com.android.server.wm.WindowManagerService;
@@ -118,6 +121,7 @@ class ServerThread extends Thread {
ContentService contentService = null;
LightsService lights = null;
PowerManagerService power = null;
+ DisplayManagerService display = null;
BatteryService battery = null;
VibratorService vibrator = null;
AlarmManagerService alarm = null;
@@ -131,11 +135,11 @@ class ServerThread extends Thread {
IPackageManager pm = null;
Context context = null;
WindowManagerService wm = null;
- BluetoothService bluetooth = null;
- BluetoothA2dpService bluetoothA2dp = null;
+ BluetoothManagerService bluetooth = null;
DockObserver dock = null;
UsbService usb = null;
SerialService serial = null;
+ TwilightService twilight = null;
UiModeManagerService uiMode = null;
RecognitionManagerService recognition = null;
ThrottleService throttle = null;
@@ -155,6 +159,10 @@ class ServerThread extends Thread {
Slog.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
+ Slog.i(TAG, "Display Manager");
+ display = new DisplayManagerService(context);
+ ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
+
Slog.i(TAG, "Telephony Registry");
ServiceManager.addService("telephony.registry", new TelephonyRegistry(context));
@@ -186,6 +194,11 @@ class ServerThread extends Thread {
}
ActivityManagerService.setSystemProcess();
+
+ Slog.i(TAG, "User Service");
+ ServiceManager.addService(Context.USER_SERVICE,
+ UserManagerService.getInstance());
+
mContentResolver = context.getContentResolver();
@@ -218,7 +231,8 @@ class ServerThread extends Thread {
// only initialize the power service after we have started the
// lights service, content providers and the battery service.
- power.init(context, lights, ActivityManagerService.self(), battery);
+ power.init(context, lights, ActivityManagerService.self(), battery,
+ BatteryStatsService.getService(), display);
Slog.i(TAG, "Alarm Manager");
alarm = new AlarmManagerService(context);
@@ -229,7 +243,7 @@ class ServerThread extends Thread {
ActivityManagerService.self());
Slog.i(TAG, "Window Manager");
- wm = WindowManagerService.main(context, power,
+ wm = WindowManagerService.main(context, power, display,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
@@ -246,23 +260,9 @@ class ServerThread extends Thread {
} else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
Slog.i(TAG, "No Bluetooth Service (factory test)");
} else {
- Slog.i(TAG, "Bluetooth Service");
- bluetooth = new BluetoothService(context);
- ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth);
- bluetooth.initAfterRegistration();
-
- if (!"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
- bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
- ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
- bluetoothA2dp);
- bluetooth.initAfterA2dpRegistration();
- }
-
- int bluetoothOn = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.BLUETOOTH_ON, 0);
- if (bluetoothOn != 0) {
- bluetooth.enable();
- }
+ Slog.i(TAG, "Bluetooth Manager Service");
+ bluetooth = new BluetoothManagerService(context);
+ ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
}
} catch (RuntimeException e) {
@@ -555,7 +555,7 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "Dock Observer");
// Listen for dock station changes
- dock = new DockObserver(context, power);
+ dock = new DockObserver(context);
} catch (Throwable e) {
reportWtf("starting DockObserver", e);
}
@@ -587,9 +587,16 @@ class ServerThread extends Thread {
}
try {
+ Slog.i(TAG, "Twilight Service");
+ twilight = new TwilightService(context);
+ } catch (Throwable e) {
+ reportWtf("starting TwilightService", e);
+ }
+
+ try {
Slog.i(TAG, "UI Mode Manager Service");
// Listen for UI mode changes
- uiMode = new UiModeManagerService(context);
+ uiMode = new UiModeManagerService(context, twilight);
} catch (Throwable e) {
reportWtf("starting UiModeManagerService", e);
}
@@ -710,6 +717,12 @@ class ServerThread extends Thread {
}
try {
+ lockSettings.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Lock Settings Service ready", e);
+ }
+
+ try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
@@ -728,16 +741,16 @@ class ServerThread extends Thread {
w.getDefaultDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);
- power.systemReady();
try {
- pm.systemReady();
+ power.systemReady(twilight);
} catch (Throwable e) {
- reportWtf("making Package Manager Service ready", e);
+ reportWtf("making Power Manager Service ready", e);
}
+
try {
- lockSettings.systemReady();
+ pm.systemReady();
} catch (Throwable e) {
- reportWtf("making Lock Settings Service ready", e);
+ reportWtf("making Package Manager Service ready", e);
}
// These are needed to propagate to the runnable below.
@@ -750,6 +763,7 @@ class ServerThread extends Thread {
final DockObserver dockF = dock;
final UsbService usbF = usb;
final ThrottleService throttleF = throttle;
+ final TwilightService twilightF = twilight;
final UiModeManagerService uiModeF = uiMode;
final AppWidgetService appWidgetF = appWidget;
final WallpaperManagerService wallpaperF = wallpaper;
@@ -763,7 +777,6 @@ class ServerThread extends Thread {
final StatusBarManagerService statusBarF = statusBar;
final DreamManagerService dreamyF = dreamy;
final InputManagerService inputManagerF = inputManager;
- final BluetoothService bluetoothF = bluetooth;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -811,6 +824,11 @@ class ServerThread extends Thread {
reportWtf("making USB Service ready", e);
}
try {
+ if (twilightF != null) twilightF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("makin Twilight Service ready", e);
+ }
+ try {
if (uiModeF != null) uiModeF.systemReady();
} catch (Throwable e) {
reportWtf("making UI Mode Service ready", e);
@@ -876,7 +894,8 @@ class ServerThread extends Thread {
reportWtf("making DreamManagerService ready", e);
}
try {
- if (inputManagerF != null) inputManagerF.systemReady(bluetoothF);
+ // TODO(BT) Pass parameter to input manager
+ if (inputManagerF != null) inputManagerF.systemReady();
} catch (Throwable e) {
reportWtf("making InputManagerService ready", e);
}
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index c23a1d9..087e8db 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -35,6 +35,7 @@ import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.List;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.NetworkInterface;
@@ -109,7 +110,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private int mOtaspMode = ServiceStateTracker.OTASP_UNKNOWN;
- private CellInfo mCellInfo = null;
+ private List<CellInfo> mCellInfo = null;
static final int PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
@@ -242,7 +243,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if ((events & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
try {
- r.callback.onCellInfoChanged(new CellInfo(mCellInfo));
+ r.callback.onCellInfoChanged(mCellInfo);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -336,7 +337,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
broadcastSignalStrengthChanged(signalStrength);
}
- public void notifyCellInfo(CellInfo cellInfo) {
+ public void notifyCellInfo(List<CellInfo> cellInfo) {
if (!checkNotifyPermission("notifyCellInfo()")) {
return;
}
@@ -346,7 +347,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
try {
- r.callback.onCellInfoChanged(new CellInfo(cellInfo));
+ r.callback.onCellInfoChanged(cellInfo);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index f35a5af..98e6dc0 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -195,6 +195,7 @@ public class ThrottleService extends IThrottleManager.Stub {
public void interfaceRemoved(String iface) {}
public void limitReached(String limitName, String iface) {}
+ public void interfaceClassDataActivityChanged(String label, boolean active) {}
}
diff --git a/services/java/com/android/server/TwilightService.java b/services/java/com/android/server/TwilightService.java
new file mode 100644
index 0000000..a7bce54
--- /dev/null
+++ b/services/java/com/android/server/TwilightService.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.Slog;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+
+import libcore.util.Objects;
+
+/**
+ * Figures out whether it's twilight time based on the user's location.
+ *
+ * Used by the UI mode manager and other components to adjust night mode
+ * effects based on sunrise and sunset.
+ */
+public final class TwilightService {
+ private static final String TAG = "TwilightService";
+
+ private static final boolean DEBUG = false;
+
+ private static final String ACTION_UPDATE_TWILIGHT_STATE =
+ "com.android.server.action.UPDATE_TWILIGHT_STATE";
+
+ private final Context mContext;
+ private final AlarmManager mAlarmManager;
+ private final LocationManager mLocationManager;
+ private final LocationHandler mLocationHandler;
+
+ private final Object mLock = new Object();
+
+ private final ArrayList<TwilightListenerRecord> mListeners =
+ new ArrayList<TwilightListenerRecord>();
+
+ private boolean mSystemReady;
+
+ private TwilightState mTwilightState;
+
+ public TwilightService(Context context) {
+ mContext = context;
+
+ mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mLocationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
+ mLocationHandler = new LocationHandler();
+ }
+
+ void systemReady() {
+ synchronized (mLock) {
+ mSystemReady = true;
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ filter.addAction(ACTION_UPDATE_TWILIGHT_STATE);
+ mContext.registerReceiver(mUpdateLocationReceiver, filter);
+
+ if (!mListeners.isEmpty()) {
+ mLocationHandler.enableLocationUpdates();
+ }
+ }
+ }
+
+ /**
+ * Gets the current twilight state.
+ *
+ * @return The current twilight state, or null if no information is available.
+ */
+ public TwilightState getCurrentState() {
+ synchronized (mLock) {
+ return mTwilightState;
+ }
+ }
+
+ /**
+ * Listens for twilight time.
+ *
+ * @param listener The listener.
+ * @param handler The handler on which to post calls into the listener.
+ */
+ public void registerListener(TwilightListener listener, Handler handler) {
+ synchronized (mLock) {
+ mListeners.add(new TwilightListenerRecord(listener, handler));
+
+ if (mSystemReady && mListeners.size() == 1) {
+ mLocationHandler.enableLocationUpdates();
+ }
+ }
+ }
+
+ private void setTwilightState(TwilightState state) {
+ synchronized (mLock) {
+ if (!Objects.equal(mTwilightState, state)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Twilight state changed: " + state);
+ }
+
+ mTwilightState = state;
+ int count = mListeners.size();
+ for (int i = 0; i < count; i++) {
+ mListeners.get(i).post();
+ }
+ }
+ }
+ }
+
+ // The user has moved if the accuracy circles of the two locations don't overlap.
+ private static boolean hasMoved(Location from, Location to) {
+ if (to == null) {
+ return false;
+ }
+
+ if (from == null) {
+ return true;
+ }
+
+ // if new location is older than the current one, the device hasn't moved.
+ if (to.getElapsedRealtimeNano() < from.getElapsedRealtimeNano()) {
+ return false;
+ }
+
+ // Get the distance between the two points.
+ float distance = from.distanceTo(to);
+
+ // Get the total accuracy radius for both locations.
+ float totalAccuracy = from.getAccuracy() + to.getAccuracy();
+
+ // If the distance is greater than the combined accuracy of the two
+ // points then they can't overlap and hence the user has moved.
+ return distance >= totalAccuracy;
+ }
+
+ /**
+ * Describes whether it is day or night.
+ * This object is immutable.
+ */
+ public static final class TwilightState {
+ private final boolean mIsNight;
+ private final long mYesterdaySunset;
+ private final long mTodaySunrise;
+ private final long mTodaySunset;
+ private final long mTomorrowSunrise;
+
+ TwilightState(boolean isNight,
+ long yesterdaySunset,
+ long todaySunrise, long todaySunset,
+ long tomorrowSunrise) {
+ mIsNight = isNight;
+ mYesterdaySunset = yesterdaySunset;
+ mTodaySunrise = todaySunrise;
+ mTodaySunset = todaySunset;
+ mTomorrowSunrise = tomorrowSunrise;
+ }
+
+ /**
+ * Returns true if it is currently night time.
+ */
+ public boolean isNight() {
+ return mIsNight;
+ }
+
+ /**
+ * Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase,
+ * or -1 if the sun never sets.
+ */
+ public long getYesterdaySunset() {
+ return mYesterdaySunset;
+ }
+
+ /**
+ * Returns the time of today's sunrise in the System.currentTimeMillis() timebase,
+ * or -1 if the sun never rises.
+ */
+ public long getTodaySunrise() {
+ return mTodaySunrise;
+ }
+
+ /**
+ * Returns the time of today's sunset in the System.currentTimeMillis() timebase,
+ * or -1 if the sun never sets.
+ */
+ public long getTodaySunset() {
+ return mTodaySunset;
+ }
+
+ /**
+ * Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase,
+ * or -1 if the sun never rises.
+ */
+ public long getTomorrowSunrise() {
+ return mTomorrowSunrise;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof TwilightState && equals((TwilightState)o);
+ }
+
+ public boolean equals(TwilightState other) {
+ return other != null
+ && mIsNight == other.mIsNight
+ && mYesterdaySunset == other.mYesterdaySunset
+ && mTodaySunrise == other.mTodaySunrise
+ && mTodaySunset == other.mTodaySunset
+ && mTomorrowSunrise == other.mTomorrowSunrise;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
+ }
+
+ @Override
+ public String toString() {
+ DateFormat f = DateFormat.getDateTimeInstance();
+ return "{TwilightState: isNight=" + mIsNight
+ + ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset))
+ + ", mTodaySunrise=" + f.format(new Date(mTodaySunrise))
+ + ", mTodaySunset=" + f.format(new Date(mTodaySunset))
+ + ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise))
+ + "}";
+ }
+ }
+
+ /**
+ * Listener for changes in twilight state.
+ */
+ public interface TwilightListener {
+ public void onTwilightStateChanged();
+ }
+
+ private static final class TwilightListenerRecord implements Runnable {
+ private final TwilightListener mListener;
+ private final Handler mHandler;
+
+ public TwilightListenerRecord(TwilightListener listener, Handler handler) {
+ mListener = listener;
+ mHandler = handler;
+ }
+
+ public void post() {
+ mHandler.post(this);
+ }
+
+ @Override
+ public void run() {
+ mListener.onTwilightStateChanged();
+ }
+ }
+
+ private final class LocationHandler extends Handler {
+ private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
+ private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
+ private static final int MSG_PROCESS_NEW_LOCATION = 3;
+ private static final int MSG_DO_TWILIGHT_UPDATE = 4;
+
+ private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
+ private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+ private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
+ private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
+ private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX =
+ 15 * DateUtils.MINUTE_IN_MILLIS;
+ private static final double FACTOR_GMT_OFFSET_LONGITUDE =
+ 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
+
+ private boolean mPassiveListenerEnabled;
+ private boolean mNetworkListenerEnabled;
+ private boolean mDidFirstInit;
+ private long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
+ private long mLastUpdateInterval;
+ private Location mLocation;
+ private final TwilightCalculator mTwilightCalculator = new TwilightCalculator();
+
+ public void processNewLocation(Location location) {
+ Message msg = obtainMessage(MSG_PROCESS_NEW_LOCATION, location);
+ sendMessage(msg);
+ }
+
+ public void enableLocationUpdates() {
+ sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
+ }
+
+ public void requestLocationUpdate() {
+ sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
+ }
+
+ public void requestTwilightUpdate() {
+ sendEmptyMessage(MSG_DO_TWILIGHT_UPDATE);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PROCESS_NEW_LOCATION: {
+ final Location location = (Location)msg.obj;
+ final boolean hasMoved = hasMoved(mLocation, location);
+ final boolean hasBetterAccuracy = mLocation == null
+ || location.getAccuracy() < mLocation.getAccuracy();
+ if (DEBUG) {
+ Slog.d(TAG, "Processing new location: " + location
+ + ", hasMoved=" + hasMoved
+ + ", hasBetterAccuracy=" + hasBetterAccuracy);
+ }
+ if (hasMoved || hasBetterAccuracy) {
+ setLocation(location);
+ }
+ break;
+ }
+
+ case MSG_GET_NEW_LOCATION_UPDATE:
+ if (!mNetworkListenerEnabled) {
+ // Don't do anything -- we are still trying to get a
+ // location.
+ return;
+ }
+ if ((mLastNetworkRegisterTime + MIN_LOCATION_UPDATE_MS) >=
+ SystemClock.elapsedRealtime()) {
+ // Don't do anything -- it hasn't been long enough
+ // since we last requested an update.
+ return;
+ }
+
+ // Unregister the current location monitor, so we can
+ // register a new one for it to get an immediate update.
+ mNetworkListenerEnabled = false;
+ mLocationManager.removeUpdates(mEmptyLocationListener);
+
+ // Fall through to re-register listener.
+ case MSG_ENABLE_LOCATION_UPDATES:
+ // enable network provider to receive at least location updates for a given
+ // distance.
+ boolean networkLocationEnabled;
+ try {
+ networkLocationEnabled =
+ mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+ } catch (Exception e) {
+ // we may get IllegalArgumentException if network location provider
+ // does not exist or is not yet installed.
+ networkLocationEnabled = false;
+ }
+ if (!mNetworkListenerEnabled && networkLocationEnabled) {
+ mNetworkListenerEnabled = true;
+ mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
+ mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+ LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
+
+ if (!mDidFirstInit) {
+ mDidFirstInit = true;
+ if (mLocation == null) {
+ retrieveLocation();
+ }
+ }
+ }
+
+ // enable passive provider to receive updates from location fixes (gps
+ // and network).
+ boolean passiveLocationEnabled;
+ try {
+ passiveLocationEnabled =
+ mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
+ } catch (Exception e) {
+ // we may get IllegalArgumentException if passive location provider
+ // does not exist or is not yet installed.
+ passiveLocationEnabled = false;
+ }
+
+ if (!mPassiveListenerEnabled && passiveLocationEnabled) {
+ mPassiveListenerEnabled = true;
+ mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+ 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
+ }
+
+ if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
+ mLastUpdateInterval *= 1.5;
+ if (mLastUpdateInterval == 0) {
+ mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
+ } else if (mLastUpdateInterval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
+ mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
+ }
+ sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval);
+ }
+ break;
+
+ case MSG_DO_TWILIGHT_UPDATE:
+ updateTwilightState();
+ break;
+ }
+ }
+
+ private void retrieveLocation() {
+ Location location = null;
+ final Iterator<String> providers =
+ mLocationManager.getProviders(new Criteria(), true).iterator();
+ while (providers.hasNext()) {
+ final Location lastKnownLocation =
+ mLocationManager.getLastKnownLocation(providers.next());
+ // pick the most recent location
+ if (location == null || (lastKnownLocation != null &&
+ location.getElapsedRealtimeNano() <
+ lastKnownLocation.getElapsedRealtimeNano())) {
+ location = lastKnownLocation;
+ }
+ }
+
+ // In the case there is no location available (e.g. GPS fix or network location
+ // is not available yet), the longitude of the location is estimated using the timezone,
+ // latitude and accuracy are set to get a good average.
+ if (location == null) {
+ Time currentTime = new Time();
+ currentTime.set(System.currentTimeMillis());
+ double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
+ (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
+ location = new Location("fake");
+ location.setLongitude(lngOffset);
+ location.setLatitude(0);
+ location.setAccuracy(417000.0f);
+ location.setTime(System.currentTimeMillis());
+ location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
+
+ if (DEBUG) {
+ Slog.d(TAG, "Estimated location from timezone: " + location);
+ }
+ }
+
+ setLocation(location);
+ }
+
+ private void setLocation(Location location) {
+ mLocation = location;
+ updateTwilightState();
+ }
+
+ private void updateTwilightState() {
+ if (mLocation == null) {
+ setTwilightState(null);
+ return;
+ }
+
+ final long now = System.currentTimeMillis();
+
+ // calculate yesterday's twilight
+ mTwilightCalculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS,
+ mLocation.getLatitude(), mLocation.getLongitude());
+ final long yesterdaySunset = mTwilightCalculator.mSunset;
+
+ // calculate today's twilight
+ mTwilightCalculator.calculateTwilight(now,
+ mLocation.getLatitude(), mLocation.getLongitude());
+ final boolean isNight = (mTwilightCalculator.mState == TwilightCalculator.NIGHT);
+ final long todaySunrise = mTwilightCalculator.mSunrise;
+ final long todaySunset = mTwilightCalculator.mSunset;
+
+ // calculate tomorrow's twilight
+ mTwilightCalculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS,
+ mLocation.getLatitude(), mLocation.getLongitude());
+ final long tomorrowSunrise = mTwilightCalculator.mSunrise;
+
+ // set twilight state
+ TwilightState state = new TwilightState(isNight, yesterdaySunset,
+ todaySunrise, todaySunset, tomorrowSunrise);
+ if (DEBUG) {
+ Slog.d(TAG, "Updating twilight state: " + state);
+ }
+ setTwilightState(state);
+
+ // schedule next update
+ long nextUpdate = 0;
+ if (todaySunrise == -1 || todaySunset == -1) {
+ // In the case the day or night never ends the update is scheduled 12 hours later.
+ nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS;
+ } else {
+ // add some extra time to be on the safe side.
+ nextUpdate += DateUtils.MINUTE_IN_MILLIS;
+
+ if (now > todaySunset) {
+ nextUpdate += tomorrowSunrise;
+ } else if (now > todaySunrise) {
+ nextUpdate += todaySunset;
+ } else {
+ nextUpdate += todaySunrise;
+ }
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Next update in " + (nextUpdate - now) + " ms");
+ }
+
+ Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
+ mAlarmManager.cancel(pendingIntent);
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
+ }
+ };
+
+ private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())
+ && !intent.getBooleanExtra("state", false)) {
+ // Airplane mode is now off!
+ mLocationHandler.requestLocationUpdate();
+ return;
+ }
+
+ // Time zone has changed or alarm expired.
+ mLocationHandler.requestTwilightUpdate();
+ }
+ };
+
+ // A LocationListener to initialize the network location provider. The location updates
+ // are handled through the passive location provider.
+ private final LocationListener mEmptyLocationListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+
+ private final LocationListener mLocationListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ mLocationHandler.processNewLocation(location);
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+}
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index d1f92a7..617b29d 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -18,7 +18,6 @@ package com.android.server;
import android.app.Activity;
import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
import android.app.IUiModeManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -32,55 +31,33 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
import android.os.BatteryManager;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.provider.Settings;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.Slog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Iterator;
import com.android.internal.R;
import com.android.internal.app.DisableCarModeActivity;
+import com.android.server.TwilightService.TwilightState;
class UiModeManagerService extends IUiModeManager.Stub {
private static final String TAG = UiModeManager.class.getSimpleName();
private static final boolean LOG = false;
- private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
-
// Enable launching of applications when entering the dock.
private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true;
private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
- private static final int MSG_UPDATE_TWILIGHT = 0;
- private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
- private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
-
- private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
- private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
- private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
- private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 15 * DateUtils.MINUTE_IN_MILLIS;
- private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
-
- private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
-
private final Context mContext;
+ private final TwilightService mTwilightService;
+ private final Handler mHandler = new Handler();
final Object mLock = new Object();
@@ -106,10 +83,6 @@ class UiModeManagerService extends IUiModeManager.Stub {
private NotificationManager mNotificationManager;
- private AlarmManager mAlarmManager;
-
- private LocationManager mLocationManager;
- private Location mLocation;
private StatusBarManager mStatusBarManager;
private final PowerManager.WakeLock mWakeLock;
@@ -206,15 +179,6 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
};
- private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
- mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
- }
- }
- };
-
private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -236,113 +200,24 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
};
- private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
+ private final TwilightService.TwilightListener mTwilightListener =
+ new TwilightService.TwilightListener() {
@Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
- if (!intent.getBooleanExtra("state", false)) {
- // Airplane mode is now off!
- mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
- }
- } else {
- // Time zone has changed!
- mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
- }
- }
- };
-
- // A LocationListener to initialize the network location provider. The location updates
- // are handled through the passive location provider.
- private final LocationListener mEmptyLocationListener = new LocationListener() {
- public void onLocationChanged(Location location) {
- }
-
- public void onProviderDisabled(String provider) {
- }
-
- public void onProviderEnabled(String provider) {
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
- };
-
- private final LocationListener mLocationListener = new LocationListener() {
-
- public void onLocationChanged(Location location) {
- final boolean hasMoved = hasMoved(location);
- final boolean hasBetterAccuracy = mLocation == null
- || location.getAccuracy() < mLocation.getAccuracy();
- if (hasMoved || hasBetterAccuracy) {
- synchronized (mLock) {
- mLocation = location;
- if (hasMoved && isDoingNightMode()
- && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
- mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
- }
- }
- }
- }
-
- public void onProviderDisabled(String provider) {
- }
-
- public void onProviderEnabled(String provider) {
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
-
- /*
- * The user has moved if the accuracy circles of the two locations
- * don't overlap.
- */
- private boolean hasMoved(Location location) {
- if (location == null) {
- return false;
- }
- if (mLocation == null) {
- return true;
- }
-
- /* if new location is older than the current one, the devices hasn't
- * moved.
- */
- if (location.getTime() < mLocation.getTime()) {
- return false;
- }
-
- /* Get the distance between the two points */
- float distance = mLocation.distanceTo(location);
-
- /* Get the total accuracy radius for both locations */
- float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
-
- /* If the distance is greater than the combined accuracy of the two
- * points then they can't overlap and hence the user has moved.
- */
- return distance >= totalAccuracy;
+ public void onTwilightStateChanged() {
+ updateTwilight();
}
};
- public UiModeManagerService(Context context) {
+ public UiModeManagerService(Context context, TwilightService twilight) {
mContext = context;
+ mTwilightService = twilight;
ServiceManager.addService(Context.UI_MODE_SERVICE, this);
- mAlarmManager =
- (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
- mLocationManager =
- (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
- mContext.registerReceiver(mTwilightUpdateReceiver,
- new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
mContext.registerReceiver(mDockModeReceiver,
new IntentFilter(Intent.ACTION_DOCK_EVENT));
mContext.registerReceiver(mBatteryReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- mContext.registerReceiver(mUpdateLocationReceiver, filter);
PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
@@ -360,6 +235,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
mNightMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
+
+ mTwilightService.registerListener(mTwilightListener, mHandler);
}
public void disableCarMode(int flags) {
@@ -419,8 +296,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
synchronized (mLock) {
mSystemReady = true;
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
+ updateComputedNightModeLocked();
updateLocked(0, 0);
- mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
}
}
@@ -467,7 +344,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
if (mCarModeEnabled) {
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
- updateTwilightLocked();
+ updateComputedNightModeLocked();
uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
: Configuration.UI_MODE_NIGHT_NO;
} else {
@@ -651,189 +528,20 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
}
- private final Handler mHandler = new Handler() {
-
- boolean mPassiveListenerEnabled;
- boolean mNetworkListenerEnabled;
- boolean mDidFirstInit;
- long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_TWILIGHT:
- synchronized (mLock) {
- if (isDoingNightMode() && mLocation != null
- && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
- updateTwilightLocked();
- updateLocked(0, 0);
- }
- }
- break;
- case MSG_GET_NEW_LOCATION_UPDATE:
- if (!mNetworkListenerEnabled) {
- // Don't do anything -- we are still trying to get a
- // location.
- return;
- }
- if ((mLastNetworkRegisterTime+MIN_LOCATION_UPDATE_MS)
- >= SystemClock.elapsedRealtime()) {
- // Don't do anything -- it hasn't been long enough
- // since we last requested an update.
- return;
- }
-
- // Unregister the current location monitor, so we can
- // register a new one for it to get an immediate update.
- mNetworkListenerEnabled = false;
- mLocationManager.removeUpdates(mEmptyLocationListener);
-
- // Fall through to re-register listener.
- case MSG_ENABLE_LOCATION_UPDATES:
- // enable network provider to receive at least location updates for a given
- // distance.
- boolean networkLocationEnabled;
- try {
- networkLocationEnabled =
- mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
- } catch (Exception e) {
- // we may get IllegalArgumentException if network location provider
- // does not exist or is not yet installed.
- networkLocationEnabled = false;
- }
- if (!mNetworkListenerEnabled && networkLocationEnabled) {
- mNetworkListenerEnabled = true;
- mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
- mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
- LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
-
- if (!mDidFirstInit) {
- mDidFirstInit = true;
- if (mLocation == null) {
- retrieveLocation();
- }
- synchronized (mLock) {
- if (isDoingNightMode() && mLocation != null
- && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
- updateTwilightLocked();
- updateLocked(0, 0);
- }
- }
- }
- }
- // enable passive provider to receive updates from location fixes (gps
- // and network).
- boolean passiveLocationEnabled;
- try {
- passiveLocationEnabled =
- mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
- } catch (Exception e) {
- // we may get IllegalArgumentException if passive location provider
- // does not exist or is not yet installed.
- passiveLocationEnabled = false;
- }
- if (!mPassiveListenerEnabled && passiveLocationEnabled) {
- mPassiveListenerEnabled = true;
- mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
- 0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
- }
- if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
- long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
- interval *= 1.5;
- if (interval == 0) {
- interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
- } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
- interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
- }
- Bundle bundle = new Bundle();
- bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
- Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
- newMsg.setData(bundle);
- mHandler.sendMessageDelayed(newMsg, interval);
- }
- break;
- }
- }
-
- private void retrieveLocation() {
- Location location = null;
- final Iterator<String> providers =
- mLocationManager.getProviders(new Criteria(), true).iterator();
- while (providers.hasNext()) {
- final Location lastKnownLocation =
- mLocationManager.getLastKnownLocation(providers.next());
- // pick the most recent location
- if (location == null || (lastKnownLocation != null &&
- location.getTime() < lastKnownLocation.getTime())) {
- location = lastKnownLocation;
- }
- }
- // In the case there is no location available (e.g. GPS fix or network location
- // is not available yet), the longitude of the location is estimated using the timezone,
- // latitude and accuracy are set to get a good average.
- if (location == null) {
- Time currentTime = new Time();
- currentTime.set(System.currentTimeMillis());
- double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
- (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
- location = new Location("fake");
- location.setLongitude(lngOffset);
- location.setLatitude(0);
- location.setAccuracy(417000.0f);
- location.setTime(System.currentTimeMillis());
- }
- synchronized (mLock) {
- mLocation = location;
+ private void updateTwilight() {
+ synchronized (mLock) {
+ if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ updateComputedNightModeLocked();
+ updateLocked(0, 0);
}
}
- };
-
- void updateTwilightLocked() {
- if (mLocation == null) {
- return;
- }
- final long currentTime = System.currentTimeMillis();
- boolean nightMode;
- // calculate current twilight
- TwilightCalculator tw = new TwilightCalculator();
- tw.calculateTwilight(currentTime,
- mLocation.getLatitude(), mLocation.getLongitude());
- if (tw.mState == TwilightCalculator.DAY) {
- nightMode = false;
- } else {
- nightMode = true;
- }
-
- // schedule next update
- long nextUpdate = 0;
- if (tw.mSunrise == -1 || tw.mSunset == -1) {
- // In the case the day or night never ends the update is scheduled 12 hours later.
- nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
- } else {
- final int mLastTwilightState = tw.mState;
- // add some extra time to be on the save side.
- nextUpdate += DateUtils.MINUTE_IN_MILLIS;
- if (currentTime > tw.mSunset) {
- // next update should be on the following day
- tw.calculateTwilight(currentTime
- + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
- mLocation.getLongitude());
- }
+ }
- if (mLastTwilightState == TwilightCalculator.NIGHT) {
- nextUpdate += tw.mSunrise;
- } else {
- nextUpdate += tw.mSunset;
- }
+ private void updateComputedNightModeLocked() {
+ TwilightState state = mTwilightService.getCurrentState();
+ if (state != null) {
+ mComputedNightMode = state.isNight();
}
-
- Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
- mAlarmManager.cancel(pendingIntent);
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
-
- mComputedNightMode = nightMode;
}
@Override
@@ -858,9 +566,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration);
pw.print(" mSystemReady="); pw.println(mSystemReady);
- if (mLocation != null) {
- pw.print(" mLocation="); pw.println(mLocation);
- }
+ pw.print(" mTwilightService.getCurrentState()=");
+ pw.println(mTwilightService.getCurrentState());
}
}
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 8a08277..afd7d0e 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -48,7 +48,7 @@ import android.os.RemoteCallbackList;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.service.wallpaper.IWallpaperConnection;
import android.service.wallpaper.IWallpaperEngine;
import android.service.wallpaper.IWallpaperService;
@@ -423,9 +423,9 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
}, userFilter);
@@ -484,7 +484,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
public void clearWallpaper() {
if (DEBUG) Slog.v(TAG, "clearWallpaper");
synchronized (mLock) {
- clearWallpaperLocked(false, UserId.getCallingUserId());
+ clearWallpaperLocked(false, UserHandle.getCallingUserId());
}
}
@@ -521,7 +521,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
public void setDimensionHints(int width, int height) throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -552,14 +552,14 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
public int getWidthHint() throws RemoteException {
synchronized (mLock) {
- WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+ WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
return wallpaper.width;
}
}
public int getHeightHint() throws RemoteException {
synchronized (mLock) {
- WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+ WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
return wallpaper.height;
}
}
@@ -574,7 +574,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (callingUid == android.os.Process.SYSTEM_UID) {
wallpaperUserId = mCurrentUserId;
} else {
- wallpaperUserId = UserId.getUserId(callingUid);
+ wallpaperUserId = UserHandle.getUserId(callingUid);
}
WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
try {
@@ -597,7 +597,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
public WallpaperInfo getWallpaperInfo() {
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper.connection != null) {
@@ -609,7 +609,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
public ParcelFileDescriptor setWallpaper(String name) {
if (DEBUG) Slog.v(TAG, "setWallpaper");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -656,7 +656,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
public void setWallpaperComponent(ComponentName name) {
if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -768,10 +768,6 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
intent.setComponent(componentName);
int serviceUserId = wallpaper.userId;
- // Because the image wallpaper is running in the system ui
- if (componentName.equals(wallpaper.imageWallpaperComponent)) {
- serviceUserId = 0;
- }
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.wallpaper_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index c239382..9edfad6 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -17,6 +17,7 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
+import com.android.server.power.PowerManagerService;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -348,7 +349,7 @@ public class Watchdog extends Thread {
}
if (mMinScreenOff >= 0 && (mPower == null ||
- mPower.timeSinceScreenOn() < mMinScreenOff)) {
+ mPower.timeSinceScreenWasLastOn() < mMinScreenOff)) {
return "screen";
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 1f03d17..f483576 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -58,6 +58,7 @@ import android.os.SystemProperties;
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
@@ -110,10 +111,6 @@ public class WifiService extends IWifiManager.Stub {
private int mScanLocksAcquired;
private int mScanLocksReleased;
- /* A mapping from UID to scan count */
- private HashMap<Integer, Integer> mScanCount =
- new HashMap<Integer, Integer>();
-
private final List<Multicaster> mMulticasters =
new ArrayList<Multicaster>();
private int mMulticastEnabled;
@@ -161,6 +158,10 @@ public class WifiService extends IWifiManager.Stub {
/* Tracks whether wifi is enabled from WifiStateMachine's perspective */
private boolean mWifiEnabled;
+ /* The work source (UID) that triggered the current WIFI scan, synchronized
+ * on this */
+ private WorkSource mScanWorkSource;
+
private boolean mIsReceiverRegistered = false;
@@ -302,6 +303,10 @@ public class WifiService extends IWifiManager.Stub {
mWifiStateMachine.sendMessage(Message.obtain(msg));
break;
}
+ case WifiManager.RSSI_PKTCNT_FETCH: {
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ break;
+ }
default: {
Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
break;
@@ -413,6 +418,7 @@ public class WifiService extends IWifiManager.Stub {
}
} else if (intent.getAction().equals(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ noteScanEnd();
checkAndSetNotification();
}
}
@@ -430,6 +436,44 @@ public class WifiService extends IWifiManager.Stub {
mNotificationEnabledSettingObserver.register();
}
+ /** Tell battery stats about a new WIFI scan */
+ private void noteScanStart() {
+ WorkSource scanWorkSource = null;
+ synchronized (WifiService.this) {
+ if (mScanWorkSource != null) {
+ // Scan already in progress, don't add this one to battery stats
+ return;
+ }
+ scanWorkSource = new WorkSource(Binder.getCallingUid());
+ mScanWorkSource = scanWorkSource;
+ }
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteWifiScanStartedFromSource(scanWorkSource);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ }
+
+ /** Tell battery stats that the current WIFI scan has completed */
+ private void noteScanEnd() {
+ WorkSource scanWorkSource = null;
+ synchronized (WifiService.this) {
+ scanWorkSource = mScanWorkSource;
+ mScanWorkSource = null;
+ }
+ if (scanWorkSource != null) {
+ try {
+ mBatteryStats.noteWifiScanStoppedFromSource(scanWorkSource);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ }
+ }
+ }
+
/**
* Check if Wi-Fi needs to be enabled and start
* if needed
@@ -541,16 +585,8 @@ public class WifiService extends IWifiManager.Stub {
*/
public void startScan(boolean forceActive) {
enforceChangePermission();
-
- int uid = Binder.getCallingUid();
- int count = 0;
- synchronized (mScanCount) {
- if (mScanCount.containsKey(uid)) {
- count = mScanCount.get(uid);
- }
- mScanCount.put(uid, ++count);
- }
mWifiStateMachine.startScan(forceActive);
+ noteScanStart();
}
private void enforceAccessPermission() {
@@ -923,10 +959,6 @@ public class WifiService extends IWifiManager.Stub {
* an AsyncChannel communication with WifiService
*/
public Messenger getWifiServiceMessenger() {
- /* Enforce the highest permissions
- TODO: when we consider exposing the asynchronous API, think about
- how to provide both access and change permissions seperately
- */
enforceAccessPermission();
enforceChangePermission();
return new Messenger(mAsyncServiceHandler);
@@ -1011,12 +1043,6 @@ public class WifiService extends IWifiManager.Stub {
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
}
- //Start scan stats tracking when device unplugged
- if (pluggedType == 0) {
- synchronized (mScanCount) {
- mScanCount.clear();
- }
- }
mPluggedType = pluggedType;
} else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
@@ -1207,13 +1233,6 @@ public class WifiService extends IWifiManager.Stub {
pw.println("Locks held:");
mLocks.dump(pw);
- pw.println("Scan count since last plugged in");
- synchronized (mScanCount) {
- for(int sc : mScanCount.keySet()) {
- pw.println("UID: " + sc + " Scan count: " + mScanCount.get(sc));
- }
- }
-
pw.println();
pw.println("WifiWatchdogStateMachine dump");
mWifiWatchdogStateMachine.dump(pw);
@@ -1333,10 +1352,8 @@ public class WifiService extends IWifiManager.Stub {
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
- break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
- mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
break;
}
}
@@ -1345,10 +1362,8 @@ public class WifiService extends IWifiManager.Stub {
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
- break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
- mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
break;
}
}
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
index 96ac493..56c0fdf 100644
--- a/services/java/com/android/server/WiredAccessoryObserver.java
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -16,12 +16,12 @@
package com.android.server;
-import android.app.ActivityManagerNative;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -39,7 +39,7 @@ import java.util.List;
/**
* <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock.
*/
-class WiredAccessoryObserver extends UEventObserver {
+final class WiredAccessoryObserver extends UEventObserver {
private static final String TAG = WiredAccessoryObserver.class.getSimpleName();
private static final boolean LOG = true;
private static final int BIT_HEADSET = (1 << 0);
@@ -50,122 +50,32 @@ class WiredAccessoryObserver extends UEventObserver {
private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
BIT_HDMI_AUDIO);
- private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
- private static class UEventInfo {
- private final String mDevName;
- private final int mState1Bits;
- private final int mState2Bits;
-
- public UEventInfo(String devName, int state1Bits, int state2Bits) {
- mDevName = devName;
- mState1Bits = state1Bits;
- mState2Bits = state2Bits;
- }
-
- public String getDevName() { return mDevName; }
-
- public String getDevPath() {
- return String.format("/devices/virtual/switch/%s", mDevName);
- }
-
- public String getSwitchStatePath() {
- return String.format("/sys/class/switch/%s/state", mDevName);
- }
-
- public boolean checkSwitchExists() {
- File f = new File(getSwitchStatePath());
- return ((null != f) && f.exists());
- }
-
- public int computeNewHeadsetState(int headsetState, int switchState) {
- int preserveMask = ~(mState1Bits | mState2Bits);
- int setBits = ((switchState == 1) ? mState1Bits :
- ((switchState == 2) ? mState2Bits : 0));
-
- return ((headsetState & preserveMask) | setBits);
- }
- }
-
- private static List<UEventInfo> makeObservedUEventList() {
- List<UEventInfo> retVal = new ArrayList<UEventInfo>();
- UEventInfo uei;
-
- // Monitor h2w
- uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC);
- if (uei.checkSwitchExists()) {
- retVal.add(uei);
- } else {
- Slog.w(TAG, "This kernel does not have wired headset support");
- }
-
- // Monitor USB
- uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL);
- if (uei.checkSwitchExists()) {
- retVal.add(uei);
- } else {
- Slog.w(TAG, "This kernel does not have usb audio support");
- }
+ private final Object mLock = new Object();
- // Monitor HDMI
- //
- // If the kernel has support for the "hdmi_audio" switch, use that. It will be signalled
- // only when the HDMI driver has a video mode configured, and the downstream sink indicates
- // support for audio in its EDID.
- //
- // If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi"
- // switch instead.
- uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0);
- if (uei.checkSwitchExists()) {
- retVal.add(uei);
- } else {
- uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0);
- if (uei.checkSwitchExists()) {
- retVal.add(uei);
- } else {
- Slog.w(TAG, "This kernel does not have HDMI audio support");
- }
- }
-
- return retVal;
- }
-
- private static List<UEventInfo> uEventInfo = makeObservedUEventList();
+ private final Context mContext;
+ private final WakeLock mWakeLock; // held while there is a pending route change
+ private final AudioManager mAudioManager;
+ private final List<UEventInfo> mUEventInfo;
private int mHeadsetState;
private int mPrevHeadsetState;
private String mHeadsetName;
- private final Context mContext;
- private final WakeLock mWakeLock; // held while there is a pending route change
-
- private final AudioManager mAudioManager;
-
public WiredAccessoryObserver(Context context) {
mContext = context;
+
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver");
mWakeLock.setReferenceCounted(false);
mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
+ mUEventInfo = makeObservedUEventList();
+
context.registerReceiver(new BootCompletedReceiver(),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
}
- private final class BootCompletedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // At any given time accessories could be inserted
- // one on the board, one on the dock and one on HDMI:
- // observe three UEVENTs
- init(); // set initial status
- for (int i = 0; i < uEventInfo.size(); ++i) {
- UEventInfo uei = uEventInfo.get(i);
- startObserving("DEVPATH="+uei.getDevPath());
- }
- }
- }
-
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
@@ -174,56 +84,64 @@ class WiredAccessoryObserver extends UEventObserver {
String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
int state = Integer.parseInt(event.get("SWITCH_STATE"));
- updateState(devPath, name, state);
+ synchronized (mLock) {
+ updateStateLocked(devPath, name, state);
+ }
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
- private synchronized final void updateState(String devPath, String name, int state)
- {
- for (int i = 0; i < uEventInfo.size(); ++i) {
- UEventInfo uei = uEventInfo.get(i);
- if (devPath.equals(uei.getDevPath())) {
- update(name, uei.computeNewHeadsetState(mHeadsetState, state));
- return;
+ private void bootCompleted() {
+ synchronized (mLock) {
+ char[] buffer = new char[1024];
+ mPrevHeadsetState = mHeadsetState;
+
+ if (LOG) Slog.v(TAG, "init()");
+
+ for (int i = 0; i < mUEventInfo.size(); ++i) {
+ UEventInfo uei = mUEventInfo.get(i);
+ try {
+ int curState;
+ FileReader file = new FileReader(uei.getSwitchStatePath());
+ int len = file.read(buffer, 0, 1024);
+ file.close();
+ curState = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ if (curState > 0) {
+ updateStateLocked(uei.getDevPath(), uei.getDevName(), curState);
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, uei.getSwitchStatePath() +
+ " not found while attempting to determine initial switch state");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
}
}
- }
-
- private synchronized final void init() {
- char[] buffer = new char[1024];
- mPrevHeadsetState = mHeadsetState;
-
- if (LOG) Slog.v(TAG, "init()");
- for (int i = 0; i < uEventInfo.size(); ++i) {
- UEventInfo uei = uEventInfo.get(i);
- try {
- int curState;
- FileReader file = new FileReader(uei.getSwitchStatePath());
- int len = file.read(buffer, 0, 1024);
- file.close();
- curState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- if (curState > 0) {
- updateState(uei.getDevPath(), uei.getDevName(), curState);
- }
+ // At any given time accessories could be inserted
+ // one on the board, one on the dock and one on HDMI:
+ // observe three UEVENTs
+ for (int i = 0; i < mUEventInfo.size(); ++i) {
+ UEventInfo uei = mUEventInfo.get(i);
+ startObserving("DEVPATH="+uei.getDevPath());
+ }
+ }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, uei.getSwitchStatePath() +
- " not found while attempting to determine initial switch state");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
+ private void updateStateLocked(String devPath, String name, int state) {
+ for (int i = 0; i < mUEventInfo.size(); ++i) {
+ UEventInfo uei = mUEventInfo.get(i);
+ if (devPath.equals(uei.getDevPath())) {
+ updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state));
+ return;
}
}
}
- private synchronized final void update(String newName, int newState) {
+ private void updateLocked(String newName, int newState) {
// Retain only relevant bits
int headsetState = newState & SUPPORTED_HEADSETS;
- int newOrOld = headsetState | mHeadsetState;
- int delay = 0;
int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
@@ -254,28 +172,26 @@ class WiredAccessoryObserver extends UEventObserver {
mHeadsetState = headsetState;
mWakeLock.acquire();
- mHandler.sendMessage(mHandler.obtainMessage(0,
- mHeadsetState,
- mPrevHeadsetState,
- mHeadsetName));
+
+ Message msg = mHandler.obtainMessage(0, mHeadsetState, mPrevHeadsetState, mHeadsetName);
+ mHandler.sendMessage(msg);
}
- private synchronized final void setDevicesState(int headsetState,
- int prevHeadsetState,
- String headsetName) {
- int allHeadsets = SUPPORTED_HEADSETS;
- for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
- if ((curHeadset & allHeadsets) != 0) {
- setDeviceState(curHeadset, headsetState, prevHeadsetState, headsetName);
- allHeadsets &= ~curHeadset;
+ private void setDevicesState(
+ int headsetState, int prevHeadsetState, String headsetName) {
+ synchronized (mLock) {
+ int allHeadsets = SUPPORTED_HEADSETS;
+ for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
+ if ((curHeadset & allHeadsets) != 0) {
+ setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName);
+ allHeadsets &= ~curHeadset;
+ }
}
}
}
- private final void setDeviceState(int headset,
- int headsetState,
- int prevHeadsetState,
- String headsetName) {
+ private void setDeviceStateLocked(int headset,
+ int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
int device;
int state;
@@ -308,11 +224,96 @@ class WiredAccessoryObserver extends UEventObserver {
}
}
- private final Handler mHandler = new Handler() {
+ private static List<UEventInfo> makeObservedUEventList() {
+ List<UEventInfo> retVal = new ArrayList<UEventInfo>();
+ UEventInfo uei;
+
+ // Monitor h2w
+ uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC);
+ if (uei.checkSwitchExists()) {
+ retVal.add(uei);
+ } else {
+ Slog.w(TAG, "This kernel does not have wired headset support");
+ }
+
+ // Monitor USB
+ uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL);
+ if (uei.checkSwitchExists()) {
+ retVal.add(uei);
+ } else {
+ Slog.w(TAG, "This kernel does not have usb audio support");
+ }
+
+ // Monitor HDMI
+ //
+ // If the kernel has support for the "hdmi_audio" switch, use that. It will be signalled
+ // only when the HDMI driver has a video mode configured, and the downstream sink indicates
+ // support for audio in its EDID.
+ //
+ // If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi"
+ // switch instead.
+ uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0);
+ if (uei.checkSwitchExists()) {
+ retVal.add(uei);
+ } else {
+ uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0);
+ if (uei.checkSwitchExists()) {
+ retVal.add(uei);
+ } else {
+ Slog.w(TAG, "This kernel does not have HDMI audio support");
+ }
+ }
+
+ return retVal;
+ }
+
+ private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
@Override
public void handleMessage(Message msg) {
setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
mWakeLock.release();
}
};
+
+ private static final class UEventInfo {
+ private final String mDevName;
+ private final int mState1Bits;
+ private final int mState2Bits;
+
+ public UEventInfo(String devName, int state1Bits, int state2Bits) {
+ mDevName = devName;
+ mState1Bits = state1Bits;
+ mState2Bits = state2Bits;
+ }
+
+ public String getDevName() { return mDevName; }
+
+ public String getDevPath() {
+ return String.format("/devices/virtual/switch/%s", mDevName);
+ }
+
+ public String getSwitchStatePath() {
+ return String.format("/sys/class/switch/%s/state", mDevName);
+ }
+
+ public boolean checkSwitchExists() {
+ File f = new File(getSwitchStatePath());
+ return ((null != f) && f.exists());
+ }
+
+ public int computeNewHeadsetState(int headsetState, int switchState) {
+ int preserveMask = ~(mState1Bits | mState2Bits);
+ int setBits = ((switchState == 1) ? mState1Bits :
+ ((switchState == 2) ? mState2Bits : 0));
+
+ return ((headsetState & preserveMask) | setBits);
+ }
+ }
+
+ private final class BootCompletedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ bootCompleted();
+ }
+ }
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 8fa6722..fc774d4 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -16,13 +16,12 @@
package com.android.server.accessibility;
-import com.android.server.input.InputFilter;
-
import android.content.Context;
import android.os.PowerManager;
import android.util.Slog;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputFilter;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index e42ec84..857334e 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -58,6 +58,7 @@ import android.text.TextUtils.SimpleStringSplitter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IWindow;
+import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -74,7 +75,6 @@ import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.server.wm.WindowManagerService;
import org.xmlpull.v1.XmlPullParserException;
@@ -157,7 +157,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private boolean mIsTouchExplorationEnabled;
- private final WindowManagerService mWindowManagerService;
+ private final IWindowManager mWindowManager;
private final SecurityPolicy mSecurityPolicy;
@@ -181,8 +181,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
- mWindowManagerService = (WindowManagerService) ServiceManager.getService(
- Context.WINDOW_SERVICE);
+ mWindowManager = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHanler();
registerPackageChangeAndBootCompletedBroadcastReceiver();
@@ -358,8 +357,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// We will update when the automation service dies.
if (mUiAutomationService == null) {
populateTouchExplorationGrantedAccessibilityServicesLocked();
- unbindAllServicesLocked();
- manageServicesLocked();
+ handleTouchExplorationGrantedAccessibilityServicesChangedLocked();
}
}
}
@@ -584,15 +582,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
*
* @param outBounds The output to which to write the bounds.
*/
- void getActiveWindowBounds(Rect outBounds) {
+ boolean getActiveWindowBounds(Rect outBounds) {
synchronized (mLock) {
final int windowId = mSecurityPolicy.mActiveWindowId;
IBinder token = mWindowIdToWindowTokenMap.get(windowId);
- mWindowManagerService.getWindowFrame(token, outBounds);
+ try {
+ mWindowManager.getWindowFrame(token, outBounds);
+ return true;
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ return false;
}
}
- int getActiveWindowId() {
+ public int getActiveWindowId() {
return mSecurityPolicy.mActiveWindowId;
}
@@ -624,7 +628,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// enabled accessibility services.
for (int i = mServices.size() - 1; i >= 0; i--) {
Service service = mServices.get(i);
- if (service.mReqeustTouchExplorationMode && service.mIsDefault == isDefault) {
+ if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
service.notifyGesture(gestureId);
return true;
}
@@ -967,13 +971,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (mInputFilter == null) {
mInputFilter = new AccessibilityInputFilter(mContext, this);
}
- mWindowManagerService.setInputFilter(mInputFilter);
+ try {
+ mWindowManager.setInputFilter(mInputFilter);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
}
return;
}
if (mHasInputFilter) {
mHasInputFilter = false;
- mWindowManagerService.setInputFilter(null);
+ try {
+ mWindowManager.setInputFilter(null);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
}
}
@@ -1000,6 +1012,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
}
+ private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked() {
+ final int serviceCount = mServices.size();
+ for (int i = 0; i < serviceCount; i++) {
+ Service service = mServices.get(i);
+ if (service.mRequestTouchExplorationMode
+ && mTouchExplorationGrantedServices.contains(service.mComponentName)) {
+ tryEnableTouchExplorationLocked(service);
+ return;
+ }
+ }
+ if (mIsTouchExplorationEnabled) {
+ mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 0,
+ 0).sendToTarget();
+ }
+ }
+
private void tryEnableTouchExplorationLocked(final Service service) {
if (!mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) {
final boolean canToggleTouchExploration = mTouchExplorationGrantedServices.contains(
@@ -1163,8 +1191,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
boolean mCanRetrieveScreenContent;
- boolean mReqeustTouchExplorationMode;
-
boolean mIsAutomation;
final Rect mTempBounds = new Rect();
@@ -1204,7 +1230,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mIsAutomation = isAutomation;
if (!isAutomation) {
mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
- mReqeustTouchExplorationMode =
+ mRequestTouchExplorationMode =
(accessibilityServiceInfo.flags
& AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
mIntent = new Intent().setComponent(mComponentName);
@@ -1334,8 +1360,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
- final int windowLeft;
- final int windowTop;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
@@ -1348,10 +1372,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return 0;
}
}
- IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId);
- mWindowManagerService.getWindowFrame(token, mTempBounds);
- windowLeft = mTempBounds.left;
- windowTop = mTempBounds.top;
}
final int flags = (mIncludeNotImportantViews) ?
AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
@@ -1359,8 +1379,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final long identityToken = Binder.clearCallingIdentity();
try {
connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId,
- windowLeft, windowTop, interactionId, callback, flags, interrogatingPid,
- interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid);
+ return getCompatibilityScale(resolvedWindowId);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
@@ -1368,7 +1388,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1377,8 +1397,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
- final int windowLeft;
- final int windowTop;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
@@ -1392,19 +1410,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return 0;
}
}
- IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId);
- mWindowManagerService.getWindowFrame(token, mTempBounds);
- windowLeft = mTempBounds.left;
- windowTop = mTempBounds.top;
}
final int flags = (mIncludeNotImportantViews) ?
AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, windowLeft,
- windowTop, interactionId, callback, flags, interrogatingPid,
+ connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
+ interactionId, callback, flags, interrogatingPid,
interrogatingTid);
+ return getCompatibilityScale(resolvedWindowId);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
@@ -1412,7 +1427,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1421,8 +1436,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid) throws RemoteException {
final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
- final int windowLeft;
- final int windowTop;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
@@ -1436,10 +1449,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return 0;
}
}
- IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId);
- mWindowManagerService.getWindowFrame(token, mTempBounds);
- windowLeft = mTempBounds.left;
- windowTop = mTempBounds.top;
}
final int allFlags = flags | ((mIncludeNotImportantViews) ?
AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0);
@@ -1447,8 +1456,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final long identityToken = Binder.clearCallingIdentity();
try {
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
- windowLeft, windowTop, interactionId, callback, allFlags, interrogatingPid,
- interrogatingTid);
+ interactionId, callback, allFlags, interrogatingPid, interrogatingTid);
+ return getCompatibilityScale(resolvedWindowId);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
@@ -1456,7 +1465,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1465,8 +1474,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
- final int windowLeft;
- final int windowTop;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
@@ -1480,18 +1487,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return 0;
}
}
- IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId);
- mWindowManagerService.getWindowFrame(token, mTempBounds);
- windowLeft = mTempBounds.left;
- windowTop = mTempBounds.top;
}
final int flags = (mIncludeNotImportantViews) ?
AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findFocus(accessibilityNodeId, focusType, windowLeft, windowTop,
- interactionId, callback, flags, interrogatingPid, interrogatingTid);
+ connection.findFocus(accessibilityNodeId, focusType, interactionId, callback,
+ flags, interrogatingPid, interrogatingTid);
+ return getCompatibilityScale(resolvedWindowId);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()");
@@ -1499,7 +1503,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1508,8 +1512,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
- final int windowLeft;
- final int windowTop;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
@@ -1523,18 +1525,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return 0;
}
}
- IBinder token = mWindowIdToWindowTokenMap.get(resolvedWindowId);
- mWindowManagerService.getWindowFrame(token, mTempBounds);
- windowLeft = mTempBounds.left;
- windowTop = mTempBounds.top;
}
final int flags = (mIncludeNotImportantViews) ?
AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.focusSearch(accessibilityNodeId, direction, windowLeft, windowTop,
- interactionId, callback, flags, interrogatingPid, interrogatingTid);
+ connection.focusSearch(accessibilityNodeId, direction, interactionId, callback,
+ flags, interrogatingPid, interrogatingTid);
+ return getCompatibilityScale(resolvedWindowId);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()");
@@ -1542,7 +1541,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(resolvedWindowId);
+ return 0;
}
@Override
@@ -1820,7 +1819,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private float getCompatibilityScale(int windowId) {
IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId);
- return mWindowManagerService.getWindowCompatibilityScale(windowToken);
+ try {
+ return mWindowManager.getWindowCompatibilityScale(windowToken);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ return 1.0f;
}
}
@@ -1939,18 +1943,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private int getFocusedWindowId() {
- // We call this only on window focus change or after touch
- // exploration gesture end and the shown windows are not that
- // many, so the linear look up is just fine.
- IBinder token = mWindowManagerService.getFocusedWindowClientToken();
- if (token != null) {
- SparseArray<IBinder> windows = mWindowIdToWindowTokenMap;
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- if (windows.valueAt(i) == token) {
- return windows.keyAt(i);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // We call this only on window focus change or after touch
+ // exploration gesture end and the shown windows are not that
+ // many, so the linear look up is just fine.
+ IBinder token = mWindowManager.getFocusedWindowToken();
+ if (token != null) {
+ SparseArray<IBinder> windows = mWindowIdToWindowTokenMap;
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ if (windows.valueAt(i) == token) {
+ return windows.keyAt(i);
+ }
}
}
+ } catch (RemoteException re) {
+ /* ignore */
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
return -1;
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 48c6b2a..ba9f2cd 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
+import android.view.InputFilter;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -37,7 +38,6 @@ import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.R;
-import com.android.server.input.InputFilter;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
new file mode 100644
index 0000000..ca7faa2
--- /dev/null
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -0,0 +1,2134 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.am.ActivityManagerService.ItemMatcher;
+import com.android.server.am.ActivityManagerService.NeededUriGrants;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+public class ActiveServices {
+ static final boolean DEBUG_SERVICE = ActivityManagerService.DEBUG_SERVICE;
+ static final boolean DEBUG_SERVICE_EXECUTING = ActivityManagerService.DEBUG_SERVICE_EXECUTING;
+ static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
+ static final String TAG = ActivityManagerService.TAG;
+ static final String TAG_MU = ActivityManagerService.TAG_MU;
+
+ // How long we wait for a service to finish executing.
+ static final int SERVICE_TIMEOUT = 20*1000;
+
+ // How long a service needs to be running until restarting its process
+ // is no longer considered to be a relaunch of the service.
+ static final int SERVICE_RESTART_DURATION = 5*1000;
+
+ // How long a service needs to be running until it will start back at
+ // SERVICE_RESTART_DURATION after being killed.
+ static final int SERVICE_RESET_RUN_DURATION = 60*1000;
+
+ // Multiplying factor to increase restart duration time by, for each time
+ // a service is killed before it has run for SERVICE_RESET_RUN_DURATION.
+ static final int SERVICE_RESTART_DURATION_FACTOR = 4;
+
+ // The minimum amount of time between restarting services that we allow.
+ // That is, when multiple services are restarting, we won't allow each
+ // to restart less than this amount of time from the last one.
+ static final int SERVICE_MIN_RESTART_TIME_BETWEEN = 10*1000;
+
+ // Maximum amount of time for there to be no activity on a service before
+ // we consider it non-essential and allow its process to go on the
+ // LRU background list.
+ static final int MAX_SERVICE_INACTIVITY = 30*60*1000;
+
+ final ActivityManagerService mAm;
+
+ final ServiceMap mServiceMap = new ServiceMap();
+
+ /**
+ * All currently bound service connections. Keys are the IBinder of
+ * the client's IServiceConnection.
+ */
+ final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections
+ = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
+
+ /**
+ * List of services that we have been asked to start,
+ * but haven't yet been able to. It is used to hold start requests
+ * while waiting for their corresponding application thread to get
+ * going.
+ */
+ final ArrayList<ServiceRecord> mPendingServices
+ = new ArrayList<ServiceRecord>();
+
+ /**
+ * List of services that are scheduled to restart following a crash.
+ */
+ final ArrayList<ServiceRecord> mRestartingServices
+ = new ArrayList<ServiceRecord>();
+
+ /**
+ * List of services that are in the process of being stopped.
+ */
+ final ArrayList<ServiceRecord> mStoppingServices
+ = new ArrayList<ServiceRecord>();
+
+ static class ServiceMap {
+
+ private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser
+ = new SparseArray<HashMap<ComponentName, ServiceRecord>>();
+ private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>>
+ mServicesByIntentPerUser = new SparseArray<
+ HashMap<Intent.FilterComparison, ServiceRecord>>();
+
+ ServiceRecord getServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser);
+ return getServices(callingUser).get(name);
+ }
+
+ ServiceRecord getServiceByName(ComponentName name) {
+ return getServiceByName(name, -1);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser);
+ return getServicesByIntent(callingUser).get(filter);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter) {
+ return getServiceByIntent(filter, -1);
+ }
+
+ void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) {
+ // TODO: Deal with global services
+ getServices(callingUser).put(name, value);
+ }
+
+ void putServiceByIntent(Intent.FilterComparison filter, int callingUser,
+ ServiceRecord value) {
+ // TODO: Deal with global services
+ getServicesByIntent(callingUser).put(filter, value);
+ }
+
+ void removeServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServices(callingUser).remove(name);
+ if (DEBUG_MU)
+ Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name
+ + " removed=" + removed);
+ }
+
+ void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServicesByIntent(callingUser).remove(filter);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter
+ + " removed=" + removed);
+ }
+
+ Collection<ServiceRecord> getAllServices(int callingUser) {
+ // TODO: Deal with global services
+ return getServices(callingUser).values();
+ }
+
+ private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) {
+ HashMap map = mServicesByNamePerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<ComponentName, ServiceRecord>();
+ mServicesByNamePerUser.put(callingUser, map);
+ }
+ return map;
+ }
+
+ private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent(
+ int callingUser) {
+ HashMap map = mServicesByIntentPerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<Intent.FilterComparison, ServiceRecord>();
+ mServicesByIntentPerUser.put(callingUser, map);
+ }
+ return map;
+ }
+ }
+
+ public ActiveServices(ActivityManagerService service) {
+ mAm = service;
+ }
+
+ ComponentName startServiceLocked(IApplicationThread caller,
+ Intent service, String resolvedType,
+ int callingPid, int callingUid, int userId) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service
+ + " type=" + resolvedType + " args=" + service.getExtras());
+
+ if (caller != null) {
+ final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+ if (callerApp == null) {
+ throw new SecurityException(
+ "Unable to find app for caller " + caller
+ + " (pid=" + Binder.getCallingPid()
+ + ") when starting service " + service);
+ }
+ }
+
+ ServiceLookupResult res =
+ retrieveServiceLocked(service, resolvedType,
+ callingPid, callingUid, userId, true);
+ if (res == null) {
+ return null;
+ }
+ if (res.record == null) {
+ return new ComponentName("!", res.permission != null
+ ? res.permission : "private to package");
+ }
+ ServiceRecord r = res.record;
+ NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
+ callingUid, r.packageName, service, service.getFlags(), null);
+ if (unscheduleServiceRestartLocked(r)) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
+ }
+ r.startRequested = true;
+ r.callStart = false;
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
+ service, neededGrants));
+ r.lastActivity = SystemClock.uptimeMillis();
+ synchronized (r.stats.getBatteryStats()) {
+ r.stats.startRunningLocked();
+ }
+ if (!bringUpServiceLocked(r, service.getFlags(), false)) {
+ return new ComponentName("!", "Service process is bad");
+ }
+ return r.name;
+ }
+
+ private void stopServiceLocked(ServiceRecord service) {
+ synchronized (service.stats.getBatteryStats()) {
+ service.stats.stopRunningLocked();
+ }
+ service.startRequested = false;
+ service.callStart = false;
+ bringDownServiceLocked(service, false);
+ }
+
+ int stopServiceLocked(IApplicationThread caller, Intent service,
+ String resolvedType, int userId) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service
+ + " type=" + resolvedType);
+
+ final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+ if (caller != null && callerApp == null) {
+ throw new SecurityException(
+ "Unable to find app for caller " + caller
+ + " (pid=" + Binder.getCallingPid()
+ + ") when stopping service " + service);
+ }
+
+ // If this service is active, make sure it is stopped.
+ ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
+ Binder.getCallingPid(), Binder.getCallingUid(), userId, false);
+ if (r != null) {
+ if (r.record != null) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ stopServiceLocked(r.record);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return 1;
+ }
+ return -1;
+ }
+
+ return 0;
+ }
+
+ IBinder peekServiceLocked(Intent service, String resolvedType) {
+ ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ UserHandle.getCallingUserId(), false);
+
+ IBinder ret = null;
+ if (r != null) {
+ // r.record is null if findServiceLocked() failed the caller permission check
+ if (r.record == null) {
+ throw new SecurityException(
+ "Permission Denial: Accessing service " + r.record.name
+ + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + r.permission);
+ }
+ IntentBindRecord ib = r.record.bindings.get(r.record.intent);
+ if (ib != null) {
+ ret = ib.binder;
+ }
+ }
+
+ return ret;
+ }
+
+ boolean stopServiceTokenLocked(ComponentName className, IBinder token,
+ int startId) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "stopServiceToken: " + className
+ + " " + token + " startId=" + startId);
+ ServiceRecord r = findServiceLocked(className, token);
+ if (r != null) {
+ if (startId >= 0) {
+ // Asked to only stop if done with all work. Note that
+ // to avoid leaks, we will take this as dropping all
+ // start items up to and including this one.
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ if (si != null) {
+ while (r.deliveredStarts.size() > 0) {
+ ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
+ cur.removeUriPermissionsLocked();
+ if (cur == si) {
+ break;
+ }
+ }
+ }
+
+ if (r.getLastStartId() != startId) {
+ return false;
+ }
+
+ if (r.deliveredStarts.size() > 0) {
+ Slog.w(TAG, "stopServiceToken startId " + startId
+ + " is last, but have " + r.deliveredStarts.size()
+ + " remaining args");
+ }
+ }
+
+ synchronized (r.stats.getBatteryStats()) {
+ r.stats.stopRunningLocked();
+ r.startRequested = false;
+ r.callStart = false;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ bringDownServiceLocked(r, false);
+ Binder.restoreCallingIdentity(origId);
+ return true;
+ }
+ return false;
+ }
+
+ public void setServiceForegroundLocked(ComponentName className, IBinder token,
+ int id, Notification notification, boolean removeNotification) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ ServiceRecord r = findServiceLocked(className, token);
+ if (r != null) {
+ if (id != 0) {
+ if (notification == null) {
+ throw new IllegalArgumentException("null notification");
+ }
+ if (r.foregroundId != id) {
+ r.cancelNotification();
+ r.foregroundId = id;
+ }
+ notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ r.foregroundNoti = notification;
+ r.isForeground = true;
+ r.postNotification();
+ if (r.app != null) {
+ updateServiceForegroundLocked(r.app, true);
+ }
+ } else {
+ if (r.isForeground) {
+ r.isForeground = false;
+ if (r.app != null) {
+ mAm.updateLruProcessLocked(r.app, false, true);
+ updateServiceForegroundLocked(r.app, true);
+ }
+ }
+ if (removeNotification) {
+ r.cancelNotification();
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
+ boolean anyForeground = false;
+ for (ServiceRecord sr : proc.services) {
+ if (sr.isForeground) {
+ anyForeground = true;
+ break;
+ }
+ }
+ if (anyForeground != proc.foregroundServices) {
+ proc.foregroundServices = anyForeground;
+ if (oomAdj) {
+ mAm.updateOomAdjLocked();
+ }
+ }
+ }
+
+ int bindServiceLocked(IApplicationThread caller, IBinder token,
+ Intent service, String resolvedType,
+ IServiceConnection connection, int flags, int userId) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
+ + " type=" + resolvedType + " conn=" + connection.asBinder()
+ + " flags=0x" + Integer.toHexString(flags));
+ if (DEBUG_MU)
+ Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
+ + Binder.getOrigCallingUid());
+ final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+ if (callerApp == null) {
+ throw new SecurityException(
+ "Unable to find app for caller " + caller
+ + " (pid=" + Binder.getCallingPid()
+ + ") when binding service " + service);
+ }
+
+ ActivityRecord activity = null;
+ if (token != null) {
+ activity = mAm.mMainStack.isInStackLocked(token);
+ if (activity == null) {
+ Slog.w(TAG, "Binding with unknown activity: " + token);
+ return 0;
+ }
+ }
+
+ int clientLabel = 0;
+ PendingIntent clientIntent = null;
+
+ if (callerApp.info.uid == Process.SYSTEM_UID) {
+ // Hacky kind of thing -- allow system stuff to tell us
+ // what they are, so we can report this elsewhere for
+ // others to know why certain services are running.
+ try {
+ clientIntent = (PendingIntent)service.getParcelableExtra(
+ Intent.EXTRA_CLIENT_INTENT);
+ } catch (RuntimeException e) {
+ }
+ if (clientIntent != null) {
+ clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
+ if (clientLabel != 0) {
+ // There are no useful extras in the intent, trash them.
+ // System code calling with this stuff just needs to know
+ // this will happen.
+ service = service.cloneFilter();
+ }
+ }
+ }
+
+ ServiceLookupResult res =
+ retrieveServiceLocked(service, resolvedType,
+ Binder.getCallingPid(), Binder.getCallingUid(), userId, true);
+ if (res == null) {
+ return 0;
+ }
+ if (res.record == null) {
+ return -1;
+ }
+ if (mAm.isSingleton(res.record.processName, res.record.appInfo,
+ res.record.serviceInfo.name, res.record.serviceInfo.flags)) {
+ userId = 0;
+ res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),
+ Binder.getCallingUid(), 0, true);
+ }
+ ServiceRecord s = res.record;
+
+ final long origId = Binder.clearCallingIdentity();
+
+ try {
+ if (unscheduleServiceRestartLocked(s)) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
+ + s);
+ }
+
+ AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
+ ConnectionRecord c = new ConnectionRecord(b, activity,
+ connection, flags, clientLabel, clientIntent);
+
+ IBinder binder = connection.asBinder();
+ ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+ if (clist == null) {
+ clist = new ArrayList<ConnectionRecord>();
+ s.connections.put(binder, clist);
+ }
+ clist.add(c);
+ b.connections.add(c);
+ if (activity != null) {
+ if (activity.connections == null) {
+ activity.connections = new HashSet<ConnectionRecord>();
+ }
+ activity.connections.add(c);
+ }
+ b.client.connections.add(c);
+ if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+ b.client.hasAboveClient = true;
+ }
+ clist = mServiceConnections.get(binder);
+ if (clist == null) {
+ clist = new ArrayList<ConnectionRecord>();
+ mServiceConnections.put(binder, clist);
+ }
+ clist.add(c);
+
+ if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+ s.lastActivity = SystemClock.uptimeMillis();
+ if (!bringUpServiceLocked(s, service.getFlags(), false)) {
+ return 0;
+ }
+ }
+
+ if (s.app != null) {
+ // This could have made the service more important.
+ mAm.updateOomAdjLocked(s.app);
+ }
+
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bind " + s + " with " + b
+ + ": received=" + b.intent.received
+ + " apps=" + b.intent.apps.size()
+ + " doRebind=" + b.intent.doRebind);
+
+ if (s.app != null && b.intent.received) {
+ // Service is already running, so we can immediately
+ // publish the connection.
+ try {
+ c.conn.connected(s.name, b.intent.binder);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure sending service " + s.shortName
+ + " to connection " + c.conn.asBinder()
+ + " (in " + c.binding.client.processName + ")", e);
+ }
+
+ // If this is the first app connected back to this binding,
+ // and the service had previously asked to be told when
+ // rebound, then do so.
+ if (b.intent.apps.size() == 1 && b.intent.doRebind) {
+ requestServiceBindingLocked(s, b.intent, true);
+ }
+ } else if (!b.intent.requested) {
+ requestServiceBindingLocked(s, b.intent, false);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return 1;
+ }
+
+ void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
+ + " " + intent + ": " + service);
+ if (r != null) {
+ Intent.FilterComparison filter
+ = new Intent.FilterComparison(intent);
+ IntentBindRecord b = r.bindings.get(filter);
+ if (b != null && !b.received) {
+ b.binder = service;
+ b.requested = true;
+ b.received = true;
+ if (r.connections.size() > 0) {
+ Iterator<ArrayList<ConnectionRecord>> it
+ = r.connections.values().iterator();
+ while (it.hasNext()) {
+ ArrayList<ConnectionRecord> clist = it.next();
+ for (int i=0; i<clist.size(); i++) {
+ ConnectionRecord c = clist.get(i);
+ if (!filter.equals(c.binding.intent.intent)) {
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Not publishing to: " + c);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Bound intent: " + c.binding.intent.intent);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Published intent: " + intent);
+ continue;
+ }
+ if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
+ try {
+ c.conn.connected(r.name, service);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure sending service " + r.name +
+ " to connection " + c.conn.asBinder() +
+ " (in " + c.binding.client.processName + ")", e);
+ }
+ }
+ }
+ }
+ }
+
+ serviceDoneExecutingLocked(r, mStoppingServices.contains(r));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ boolean unbindServiceLocked(IServiceConnection connection) {
+ IBinder binder = connection.asBinder();
+ if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
+ ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+ if (clist == null) {
+ Slog.w(TAG, "Unbind failed: could not find connection for "
+ + connection.asBinder());
+ return false;
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ while (clist.size() > 0) {
+ ConnectionRecord r = clist.get(0);
+ removeConnectionLocked(r, null, null);
+
+ if (r.binding.service.app != null) {
+ // This could have made the service less important.
+ mAm.updateOomAdjLocked(r.binding.service.app);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return true;
+ }
+
+ void unbindFinishedLocked(ServiceRecord r, Intent intent, boolean doRebind) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ if (r != null) {
+ Intent.FilterComparison filter
+ = new Intent.FilterComparison(intent);
+ IntentBindRecord b = r.bindings.get(filter);
+ if (DEBUG_SERVICE) Slog.v(TAG, "unbindFinished in " + r
+ + " at " + b + ": apps="
+ + (b != null ? b.apps.size() : 0));
+
+ boolean inStopping = mStoppingServices.contains(r);
+ if (b != null) {
+ if (b.apps.size() > 0 && !inStopping) {
+ // Applications have already bound since the last
+ // unbind, so just rebind right here.
+ requestServiceBindingLocked(r, b, true);
+ } else {
+ // Note to tell the service the next time there is
+ // a new client.
+ b.doRebind = true;
+ }
+ }
+
+ serviceDoneExecutingLocked(r, inStopping);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private final ServiceRecord findServiceLocked(ComponentName name,
+ IBinder token) {
+ ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
+ return r == token ? r : null;
+ }
+
+ private final class ServiceLookupResult {
+ final ServiceRecord record;
+ final String permission;
+
+ ServiceLookupResult(ServiceRecord _record, String _permission) {
+ record = _record;
+ permission = _permission;
+ }
+ }
+
+ private class ServiceRestarter implements Runnable {
+ private ServiceRecord mService;
+
+ void setService(ServiceRecord service) {
+ mService = service;
+ }
+
+ public void run() {
+ synchronized(mAm) {
+ performServiceRestartLocked(mService);
+ }
+ }
+ }
+
+ private ServiceLookupResult retrieveServiceLocked(Intent service,
+ String resolvedType, int callingPid, int callingUid, int userId,
+ boolean createIfNeeded) {
+ ServiceRecord r = null;
+ if (DEBUG_SERVICE) Slog.v(TAG, "retrieveServiceLocked: " + service
+ + " type=" + resolvedType + " callingUid=" + callingUid);
+
+ if (service.getComponent() != null) {
+ r = mServiceMap.getServiceByName(service.getComponent(), userId);
+ }
+ if (r == null) {
+ Intent.FilterComparison filter = new Intent.FilterComparison(service);
+ r = mServiceMap.getServiceByIntent(filter, userId);
+ }
+ if (r == null) {
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveService(
+ service, resolvedType,
+ ActivityManagerService.STOCK_PM_FLAGS, userId);
+ ServiceInfo sInfo =
+ rInfo != null ? rInfo.serviceInfo : null;
+ if (sInfo == null) {
+ Slog.w(TAG, "Unable to start service " + service + " U=" + userId +
+ ": not found");
+ return null;
+ }
+ ComponentName name = new ComponentName(
+ sInfo.applicationInfo.packageName, sInfo.name);
+ if (userId > 0) {
+ if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
+ sInfo.name, sInfo.flags)) {
+ userId = 0;
+ }
+ sInfo = new ServiceInfo(sInfo);
+ sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
+ }
+ r = mServiceMap.getServiceByName(name, userId);
+ if (r == null && createIfNeeded) {
+ Intent.FilterComparison filter = new Intent.FilterComparison(
+ service.cloneFilter());
+ ServiceRestarter res = new ServiceRestarter();
+ BatteryStatsImpl.Uid.Pkg.Serv ss = null;
+ BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ ss = stats.getServiceStatsLocked(
+ sInfo.applicationInfo.uid, sInfo.packageName,
+ sInfo.name);
+ }
+ r = new ServiceRecord(mAm, ss, name, filter, sInfo, res);
+ res.setService(r);
+ mServiceMap.putServiceByName(name, UserHandle.getUserId(r.appInfo.uid), r);
+ mServiceMap.putServiceByIntent(filter, UserHandle.getUserId(r.appInfo.uid), r);
+
+ // Make sure this component isn't in the pending list.
+ int N = mPendingServices.size();
+ for (int i=0; i<N; i++) {
+ ServiceRecord pr = mPendingServices.get(i);
+ if (pr.name.equals(name)) {
+ mPendingServices.remove(i);
+ i--;
+ N--;
+ }
+ }
+ }
+ } catch (RemoteException ex) {
+ // pm is in same process, this will never happen.
+ }
+ }
+ if (r != null) {
+ if (mAm.checkComponentPermission(r.permission,
+ callingPid, callingUid, r.appInfo.uid, r.exported)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (!r.exported) {
+ Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ + " from pid=" + callingPid
+ + ", uid=" + callingUid
+ + " that is not exported from uid " + r.appInfo.uid);
+ return new ServiceLookupResult(null, "not exported from uid "
+ + r.appInfo.uid);
+ }
+ Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ + " from pid=" + callingPid
+ + ", uid=" + callingUid
+ + " requires " + r.permission);
+ return new ServiceLookupResult(null, r.permission);
+ }
+ return new ServiceLookupResult(r, null);
+ }
+ return null;
+ }
+
+ private final void bumpServiceExecutingLocked(ServiceRecord r, String why) {
+ if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING "
+ + why + " of " + r + " in app " + r.app);
+ else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING "
+ + why + " of " + r.shortName);
+ long now = SystemClock.uptimeMillis();
+ if (r.executeNesting == 0 && r.app != null) {
+ if (r.app.executingServices.size() == 0) {
+ Message msg = mAm.mHandler.obtainMessage(
+ ActivityManagerService.SERVICE_TIMEOUT_MSG);
+ msg.obj = r.app;
+ mAm.mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
+ }
+ r.app.executingServices.add(r);
+ }
+ r.executeNesting++;
+ r.executingStart = now;
+ }
+
+ private final boolean requestServiceBindingLocked(ServiceRecord r,
+ IntentBindRecord i, boolean rebind) {
+ if (r.app == null || r.app.thread == null) {
+ // If service is not currently running, can't yet bind.
+ return false;
+ }
+ if ((!i.requested || rebind) && i.apps.size() > 0) {
+ try {
+ bumpServiceExecutingLocked(r, "bind");
+ r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
+ if (!rebind) {
+ i.requested = true;
+ }
+ i.hasBound = true;
+ i.doRebind = false;
+ } catch (RemoteException e) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private final boolean scheduleServiceRestartLocked(ServiceRecord r,
+ boolean allowCancel) {
+ boolean canceled = false;
+
+ final long now = SystemClock.uptimeMillis();
+
+ if ((r.serviceInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ long minDuration = SERVICE_RESTART_DURATION;
+ long resetTime = SERVICE_RESET_RUN_DURATION;
+
+ // Any delivered but not yet finished starts should be put back
+ // on the pending list.
+ final int N = r.deliveredStarts.size();
+ if (N > 0) {
+ for (int i=N-1; i>=0; i--) {
+ ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+ si.removeUriPermissionsLocked();
+ if (si.intent == null) {
+ // We'll generate this again if needed.
+ } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
+ && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
+ r.pendingStarts.add(0, si);
+ long dur = SystemClock.uptimeMillis() - si.deliveredTime;
+ dur *= 2;
+ if (minDuration < dur) minDuration = dur;
+ if (resetTime < dur) resetTime = dur;
+ } else {
+ Slog.w(TAG, "Canceling start item " + si.intent + " in service "
+ + r.name);
+ canceled = true;
+ }
+ }
+ r.deliveredStarts.clear();
+ }
+
+ r.totalRestartCount++;
+ if (r.restartDelay == 0) {
+ r.restartCount++;
+ r.restartDelay = minDuration;
+ } else {
+ // If it has been a "reasonably long time" since the service
+ // was started, then reset our restart duration back to
+ // the beginning, so we don't infinitely increase the duration
+ // on a service that just occasionally gets killed (which is
+ // a normal case, due to process being killed to reclaim memory).
+ if (now > (r.restartTime+resetTime)) {
+ r.restartCount = 1;
+ r.restartDelay = minDuration;
+ } else {
+ if ((r.serviceInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_PERSISTENT) != 0) {
+ // Services in peristent processes will restart much more
+ // quickly, since they are pretty important. (Think SystemUI).
+ r.restartDelay += minDuration/2;
+ } else {
+ r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
+ if (r.restartDelay < minDuration) {
+ r.restartDelay = minDuration;
+ }
+ }
+ }
+ }
+
+ r.nextRestartTime = now + r.restartDelay;
+
+ // Make sure that we don't end up restarting a bunch of services
+ // all at the same time.
+ boolean repeat;
+ do {
+ repeat = false;
+ for (int i=mRestartingServices.size()-1; i>=0; i--) {
+ ServiceRecord r2 = mRestartingServices.get(i);
+ if (r2 != r && r.nextRestartTime
+ >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
+ && r.nextRestartTime
+ < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
+ r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
+ r.restartDelay = r.nextRestartTime - now;
+ repeat = true;
+ break;
+ }
+ }
+ } while (repeat);
+
+ } else {
+ // Persistent processes are immediately restrted, so there is no
+ // reason to hold of on restarting their services.
+ r.totalRestartCount++;
+ r.restartCount = 0;
+ r.restartDelay = 0;
+ r.nextRestartTime = now;
+ }
+
+ if (!mRestartingServices.contains(r)) {
+ mRestartingServices.add(r);
+ }
+
+ r.cancelNotification();
+
+ mAm.mHandler.removeCallbacks(r.restarter);
+ mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
+ r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
+ Slog.w(TAG, "Scheduling restart of crashed service "
+ + r.shortName + " in " + r.restartDelay + "ms");
+ EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
+ r.shortName, r.restartDelay);
+
+ return canceled;
+ }
+
+ final void performServiceRestartLocked(ServiceRecord r) {
+ if (!mRestartingServices.contains(r)) {
+ return;
+ }
+ bringUpServiceLocked(r, r.intent.getIntent().getFlags(), true);
+ }
+
+ private final boolean unscheduleServiceRestartLocked(ServiceRecord r) {
+ if (r.restartDelay == 0) {
+ return false;
+ }
+ r.resetRestartCounter();
+ mRestartingServices.remove(r);
+ mAm.mHandler.removeCallbacks(r.restarter);
+ return true;
+ }
+
+ private final boolean bringUpServiceLocked(ServiceRecord r,
+ int intentFlags, boolean whileRestarting) {
+ //Slog.i(TAG, "Bring up service:");
+ //r.dump(" ");
+
+ if (r.app != null && r.app.thread != null) {
+ sendServiceArgsLocked(r, false);
+ return true;
+ }
+
+ if (!whileRestarting && r.restartDelay > 0) {
+ // If waiting for a restart, then do nothing.
+ return true;
+ }
+
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
+
+ // We are now bringing the service up, so no longer in the
+ // restarting state.
+ mRestartingServices.remove(r);
+
+ // Service is now being launched, its package can't be stopped.
+ try {
+ AppGlobals.getPackageManager().setPackageStoppedState(
+ r.packageName, false, r.userId);
+ } catch (RemoteException e) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + r.packageName + ": " + e);
+ }
+
+ final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
+ final String procName = r.processName;
+ ProcessRecord app;
+
+ if (!isolated) {
+ app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
+ if (app != null && app.thread != null) {
+ try {
+ app.addPackage(r.appInfo.packageName);
+ realStartServiceLocked(r, app);
+ return true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when starting service " + r.shortName, e);
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+ } else {
+ // If this service runs in an isolated process, then each time
+ // we call startProcessLocked() we will get a new isolated
+ // process, starting another process if we are currently waiting
+ // for a previous process to come up. To deal with this, we store
+ // in the service any current isolated process it is running in or
+ // waiting to have come up.
+ app = r.isolatedProc;
+ }
+
+ // Not running -- get it started, and enqueue this service record
+ // to be executed when the app comes up.
+ if (app == null) {
+ if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
+ "service", r.name, false, isolated)) == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + r.appInfo.packageName + "/"
+ + r.appInfo.uid + " for service "
+ + r.intent.getIntent() + ": process is bad");
+ bringDownServiceLocked(r, true);
+ return false;
+ }
+ if (isolated) {
+ r.isolatedProc = app;
+ }
+ }
+
+ if (!mPendingServices.contains(r)) {
+ mPendingServices.add(r);
+ }
+
+ return true;
+ }
+
+ private final void requestServiceBindingsLocked(ServiceRecord r) {
+ Iterator<IntentBindRecord> bindings = r.bindings.values().iterator();
+ while (bindings.hasNext()) {
+ IntentBindRecord i = bindings.next();
+ if (!requestServiceBindingLocked(r, i, false)) {
+ break;
+ }
+ }
+ }
+
+ private final void realStartServiceLocked(ServiceRecord r,
+ ProcessRecord app) throws RemoteException {
+ if (app.thread == null) {
+ throw new RemoteException();
+ }
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ + ", ProcessRecord.uid = " + app.uid);
+ r.app = app;
+ r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
+
+ app.services.add(r);
+ bumpServiceExecutingLocked(r, "create");
+ mAm.updateLruProcessLocked(app, true, true);
+
+ boolean created = false;
+ try {
+ mAm.mStringBuilder.setLength(0);
+ r.intent.getIntent().toShortString(mAm.mStringBuilder, true, false, true, false);
+ EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
+ System.identityHashCode(r), r.shortName,
+ mAm.mStringBuilder.toString(), r.app.pid);
+ synchronized (r.stats.getBatteryStats()) {
+ r.stats.startLaunchedLocked();
+ }
+ mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
+ app.thread.scheduleCreateService(r, r.serviceInfo,
+ mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
+ r.postNotification();
+ created = true;
+ } finally {
+ if (!created) {
+ app.services.remove(r);
+ scheduleServiceRestartLocked(r, false);
+ }
+ }
+
+ requestServiceBindingsLocked(r);
+
+ // If the service is in the started state, and there are no
+ // pending arguments, then fake up one so its onStartCommand() will
+ // be called.
+ if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
+ null, null));
+ }
+
+ sendServiceArgsLocked(r, true);
+ }
+
+ private final void sendServiceArgsLocked(ServiceRecord r,
+ boolean oomAdjusted) {
+ final int N = r.pendingStarts.size();
+ if (N == 0) {
+ return;
+ }
+
+ while (r.pendingStarts.size() > 0) {
+ try {
+ ServiceRecord.StartItem si = r.pendingStarts.remove(0);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: "
+ + r + " " + r.intent + " args=" + si.intent);
+ if (si.intent == null && N > 1) {
+ // If somehow we got a dummy null intent in the middle,
+ // then skip it. DO NOT skip a null intent when it is
+ // the only one in the list -- this is to support the
+ // onStartCommand(null) case.
+ continue;
+ }
+ si.deliveredTime = SystemClock.uptimeMillis();
+ r.deliveredStarts.add(si);
+ si.deliveryCount++;
+ if (si.neededGrants != null) {
+ mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
+ si.getUriPermissionsLocked());
+ }
+ bumpServiceExecutingLocked(r, "start");
+ if (!oomAdjusted) {
+ oomAdjusted = true;
+ mAm.updateOomAdjLocked(r.app);
+ }
+ int flags = 0;
+ if (si.deliveryCount > 1) {
+ flags |= Service.START_FLAG_RETRY;
+ }
+ if (si.doneExecutingCount > 0) {
+ flags |= Service.START_FLAG_REDELIVERY;
+ }
+ r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
+ } catch (RemoteException e) {
+ // Remote process gone... we'll let the normal cleanup take
+ // care of this.
+ if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r);
+ break;
+ } catch (Exception e) {
+ Slog.w(TAG, "Unexpected exception", e);
+ break;
+ }
+ }
+ }
+
+ private final void bringDownServiceLocked(ServiceRecord r, boolean force) {
+ //Slog.i(TAG, "Bring down service:");
+ //r.dump(" ");
+
+ // Does it still need to run?
+ if (!force && r.startRequested) {
+ return;
+ }
+ if (r.connections.size() > 0) {
+ if (!force) {
+ // XXX should probably keep a count of the number of auto-create
+ // connections directly in the service.
+ Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
+ while (it.hasNext()) {
+ ArrayList<ConnectionRecord> cr = it.next();
+ for (int i=0; i<cr.size(); i++) {
+ if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+ return;
+ }
+ }
+ }
+ }
+
+ // Report to all of the connections that the service is no longer
+ // available.
+ Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
+ while (it.hasNext()) {
+ ArrayList<ConnectionRecord> c = it.next();
+ for (int i=0; i<c.size(); i++) {
+ ConnectionRecord cr = c.get(i);
+ // There is still a connection to the service that is
+ // being brought down. Mark it as dead.
+ cr.serviceDead = true;
+ try {
+ cr.conn.connected(r.name, null);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure disconnecting service " + r.name +
+ " to connection " + c.get(i).conn.asBinder() +
+ " (in " + c.get(i).binding.client.processName + ")", e);
+ }
+ }
+ }
+ }
+
+ // Tell the service that it has been unbound.
+ if (r.bindings.size() > 0 && r.app != null && r.app.thread != null) {
+ Iterator<IntentBindRecord> it = r.bindings.values().iterator();
+ while (it.hasNext()) {
+ IntentBindRecord ibr = it.next();
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr
+ + ": hasBound=" + ibr.hasBound);
+ if (r.app != null && r.app.thread != null && ibr.hasBound) {
+ try {
+ bumpServiceExecutingLocked(r, "bring down unbind");
+ mAm.updateOomAdjLocked(r.app);
+ ibr.hasBound = false;
+ r.app.thread.scheduleUnbindService(r,
+ ibr.intent.getIntent());
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when unbinding service "
+ + r.shortName, e);
+ serviceDoneExecutingLocked(r, true);
+ }
+ }
+ }
+ }
+
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
+ EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
+ System.identityHashCode(r), r.shortName,
+ (r.app != null) ? r.app.pid : -1);
+
+ mServiceMap.removeServiceByName(r.name, r.userId);
+ mServiceMap.removeServiceByIntent(r.intent, r.userId);
+ r.totalRestartCount = 0;
+ unscheduleServiceRestartLocked(r);
+
+ // Also make sure it is not on the pending list.
+ int N = mPendingServices.size();
+ for (int i=0; i<N; i++) {
+ if (mPendingServices.get(i) == r) {
+ mPendingServices.remove(i);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
+ i--;
+ N--;
+ }
+ }
+
+ r.cancelNotification();
+ r.isForeground = false;
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+
+ // Clear start entries.
+ r.clearDeliveredStartsLocked();
+ r.pendingStarts.clear();
+
+ if (r.app != null) {
+ synchronized (r.stats.getBatteryStats()) {
+ r.stats.stopLaunchedLocked();
+ }
+ r.app.services.remove(r);
+ if (r.app.thread != null) {
+ try {
+ bumpServiceExecutingLocked(r, "stop");
+ mStoppingServices.add(r);
+ mAm.updateOomAdjLocked(r.app);
+ r.app.thread.scheduleStopService(r);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when stopping service "
+ + r.shortName, e);
+ serviceDoneExecutingLocked(r, true);
+ }
+ updateServiceForegroundLocked(r.app, false);
+ } else {
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Removed service that has no process: " + r);
+ }
+ } else {
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Removed service that is not running: " + r);
+ }
+
+ if (r.bindings.size() > 0) {
+ r.bindings.clear();
+ }
+
+ if (r.restarter instanceof ServiceRestarter) {
+ ((ServiceRestarter)r.restarter).setService(null);
+ }
+ }
+
+ void removeConnectionLocked(
+ ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
+ IBinder binder = c.conn.asBinder();
+ AppBindRecord b = c.binding;
+ ServiceRecord s = b.service;
+ ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+ if (clist != null) {
+ clist.remove(c);
+ if (clist.size() == 0) {
+ s.connections.remove(binder);
+ }
+ }
+ b.connections.remove(c);
+ if (c.activity != null && c.activity != skipAct) {
+ if (c.activity.connections != null) {
+ c.activity.connections.remove(c);
+ }
+ }
+ if (b.client != skipApp) {
+ b.client.connections.remove(c);
+ if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+ b.client.updateHasAboveClientLocked();
+ }
+ }
+ clist = mServiceConnections.get(binder);
+ if (clist != null) {
+ clist.remove(c);
+ if (clist.size() == 0) {
+ mServiceConnections.remove(binder);
+ }
+ }
+
+ if (b.connections.size() == 0) {
+ b.intent.apps.remove(b.client);
+ }
+
+ if (!c.serviceDead) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "Disconnecting binding " + b.intent
+ + ": shouldUnbind=" + b.intent.hasBound);
+ if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
+ && b.intent.hasBound) {
+ try {
+ bumpServiceExecutingLocked(s, "unbind");
+ mAm.updateOomAdjLocked(s.app);
+ b.intent.hasBound = false;
+ // Assume the client doesn't want to know about a rebind;
+ // we will deal with that later if it asks for one.
+ b.intent.doRebind = false;
+ s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
+ serviceDoneExecutingLocked(s, true);
+ }
+ }
+
+ if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
+ bringDownServiceLocked(s, false);
+ }
+ }
+ }
+
+ void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
+ boolean inStopping = mStoppingServices.contains(r);
+ if (r != null) {
+ if (type == 1) {
+ // This is a call from a service start... take care of
+ // book-keeping.
+ r.callStart = true;
+ switch (res) {
+ case Service.START_STICKY_COMPATIBILITY:
+ case Service.START_STICKY: {
+ // We are done with the associated start arguments.
+ r.findDeliveredStart(startId, true);
+ // Don't stop if killed.
+ r.stopIfKilled = false;
+ break;
+ }
+ case Service.START_NOT_STICKY: {
+ // We are done with the associated start arguments.
+ r.findDeliveredStart(startId, true);
+ if (r.getLastStartId() == startId) {
+ // There is no more work, and this service
+ // doesn't want to hang around if killed.
+ r.stopIfKilled = true;
+ }
+ break;
+ }
+ case Service.START_REDELIVER_INTENT: {
+ // We'll keep this item until they explicitly
+ // call stop for it, but keep track of the fact
+ // that it was delivered.
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ if (si != null) {
+ si.deliveryCount = 0;
+ si.doneExecutingCount++;
+ // Don't stop if killed.
+ r.stopIfKilled = true;
+ }
+ break;
+ }
+ case Service.START_TASK_REMOVED_COMPLETE: {
+ // Special processing for onTaskRemoved(). Don't
+ // impact normal onStartCommand() processing.
+ r.findDeliveredStart(startId, true);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Unknown service start result: " + res);
+ }
+ if (res == Service.START_STICKY_COMPATIBILITY) {
+ r.callStart = false;
+ }
+ }
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
+ + Binder.getOrigCallingUid());
+ final long origId = Binder.clearCallingIdentity();
+ serviceDoneExecutingLocked(r, inStopping);
+ Binder.restoreCallingIdentity(origId);
+ } else {
+ Slog.w(TAG, "Done executing unknown service from pid "
+ + Binder.getCallingPid());
+ }
+ }
+
+ private void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
+ + ": nesting=" + r.executeNesting
+ + ", inStopping=" + inStopping + ", app=" + r.app);
+ else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
+ r.executeNesting--;
+ if (r.executeNesting <= 0 && r.app != null) {
+ if (DEBUG_SERVICE) Slog.v(TAG,
+ "Nesting at 0 of " + r.shortName);
+ r.app.executingServices.remove(r);
+ if (r.app.executingServices.size() == 0) {
+ if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
+ "No more executingServices of " + r.shortName);
+ mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
+ }
+ if (inStopping) {
+ if (DEBUG_SERVICE) Slog.v(TAG,
+ "doneExecuting remove stopping " + r);
+ mStoppingServices.remove(r);
+ r.bindings.clear();
+ }
+ mAm.updateOomAdjLocked(r.app);
+ }
+ }
+
+ boolean attachApplicationLocked(ProcessRecord proc, String processName) throws Exception {
+ boolean didSomething = false;
+ // Collect any services that are waiting for this process to come up.
+ if (mPendingServices.size() > 0) {
+ ServiceRecord sr = null;
+ try {
+ for (int i=0; i<mPendingServices.size(); i++) {
+ sr = mPendingServices.get(i);
+ if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+ || !processName.equals(sr.processName))) {
+ continue;
+ }
+
+ mPendingServices.remove(i);
+ i--;
+ realStartServiceLocked(sr, proc);
+ didSomething = true;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception in new application when starting service "
+ + sr.shortName, e);
+ throw e;
+ }
+ }
+ // Also, if there are any services that are waiting to restart and
+ // would run in this process, now is a good time to start them. It would
+ // be weird to bring up the process but arbitrarily not let the services
+ // run at this point just because their restart time hasn't come up.
+ if (mRestartingServices.size() > 0) {
+ ServiceRecord sr = null;
+ for (int i=0; i<mRestartingServices.size(); i++) {
+ sr = mRestartingServices.get(i);
+ if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+ || !processName.equals(sr.processName))) {
+ continue;
+ }
+ mAm.mHandler.removeCallbacks(sr.restarter);
+ mAm.mHandler.post(sr.restarter);
+ }
+ }
+ return didSomething;
+ }
+
+ void processStartTimedOutLocked(ProcessRecord proc) {
+ for (int i=0; i<mPendingServices.size(); i++) {
+ ServiceRecord sr = mPendingServices.get(i);
+ if ((proc.uid == sr.appInfo.uid
+ && proc.processName.equals(sr.processName))
+ || sr.isolatedProc == proc) {
+ Slog.w(TAG, "Forcing bringing down service: " + sr);
+ sr.isolatedProc = null;
+ mPendingServices.remove(i);
+ i--;
+ bringDownServiceLocked(sr, true);
+ }
+ }
+ }
+
+ boolean forceStopLocked(String name, int userId, boolean evenPersistent, boolean doit) {
+ boolean didSomething = false;
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
+ if (service.packageName.equals(name)
+ && (service.app == null || evenPersistent || !service.app.persistent)) {
+ if (!doit) {
+ return true;
+ }
+ didSomething = true;
+ Slog.i(TAG, " Force stopping service " + service);
+ if (service.app != null) {
+ service.app.removed = true;
+ }
+ service.app = null;
+ service.isolatedProc = null;
+ services.add(service);
+ }
+ }
+
+ int N = services.size();
+ for (int i=0; i<N; i++) {
+ bringDownServiceLocked(services.get(i), true);
+ }
+ return didSomething;
+ }
+
+ void cleanUpRemovedTaskLocked(TaskRecord tr, ComponentName component, Intent baseIntent) {
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ for (ServiceRecord sr : mServiceMap.getAllServices(tr.userId)) {
+ if (sr.packageName.equals(component.getPackageName())) {
+ services.add(sr);
+ }
+ }
+
+ // Take care of any running services associated with the app.
+ for (int i=0; i<services.size(); i++) {
+ ServiceRecord sr = services.get(i);
+ if (sr.startRequested) {
+ if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
+ Slog.i(TAG, "Stopping service " + sr.shortName + ": remove task");
+ stopServiceLocked(sr);
+ } else {
+ sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
+ sr.makeNextStartId(), baseIntent, null));
+ if (sr.app != null && sr.app.thread != null) {
+ sendServiceArgsLocked(sr, false);
+ }
+ }
+ }
+ }
+ }
+
+ final void killServicesLocked(ProcessRecord app,
+ boolean allowRestart) {
+ // Report disconnected services.
+ if (false) {
+ // XXX we are letting the client link to the service for
+ // death notifications.
+ if (app.services.size() > 0) {
+ Iterator<ServiceRecord> it = app.services.iterator();
+ while (it.hasNext()) {
+ ServiceRecord r = it.next();
+ if (r.connections.size() > 0) {
+ Iterator<ArrayList<ConnectionRecord>> jt
+ = r.connections.values().iterator();
+ while (jt.hasNext()) {
+ ArrayList<ConnectionRecord> cl = jt.next();
+ for (int i=0; i<cl.size(); i++) {
+ ConnectionRecord c = cl.get(i);
+ if (c.binding.client != app) {
+ try {
+ //c.conn.connected(r.className, null);
+ } catch (Exception e) {
+ // todo: this should be asynchronous!
+ Slog.w(TAG, "Exception thrown disconnected servce "
+ + r.shortName
+ + " from app " + app.processName, e);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Clean up any connections this application has to other services.
+ if (app.connections.size() > 0) {
+ Iterator<ConnectionRecord> it = app.connections.iterator();
+ while (it.hasNext()) {
+ ConnectionRecord r = it.next();
+ removeConnectionLocked(r, app, null);
+ }
+ }
+ app.connections.clear();
+
+ if (app.services.size() != 0) {
+ // Any services running in the application need to be placed
+ // back in the pending list.
+ Iterator<ServiceRecord> it = app.services.iterator();
+ while (it.hasNext()) {
+ ServiceRecord sr = it.next();
+ synchronized (sr.stats.getBatteryStats()) {
+ sr.stats.stopLaunchedLocked();
+ }
+ sr.app = null;
+ sr.isolatedProc = null;
+ sr.executeNesting = 0;
+ if (mStoppingServices.remove(sr)) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+ }
+
+ boolean hasClients = sr.bindings.size() > 0;
+ if (hasClients) {
+ Iterator<IntentBindRecord> bindings
+ = sr.bindings.values().iterator();
+ while (bindings.hasNext()) {
+ IntentBindRecord b = bindings.next();
+ if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
+ + ": shouldUnbind=" + b.hasBound);
+ b.binder = null;
+ b.requested = b.received = b.hasBound = false;
+ }
+ }
+
+ if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ Slog.w(TAG, "Service crashed " + sr.crashCount
+ + " times, stopping: " + sr);
+ EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
+ sr.crashCount, sr.shortName, app.pid);
+ bringDownServiceLocked(sr, true);
+ } else if (!allowRestart) {
+ bringDownServiceLocked(sr, true);
+ } else {
+ boolean canceled = scheduleServiceRestartLocked(sr, true);
+
+ // Should the service remain running? Note that in the
+ // extreme case of so many attempts to deliver a command
+ // that it failed we also will stop it here.
+ if (sr.startRequested && (sr.stopIfKilled || canceled)) {
+ if (sr.pendingStarts.size() == 0) {
+ sr.startRequested = false;
+ if (!hasClients) {
+ // Whoops, no reason to restart!
+ bringDownServiceLocked(sr, true);
+ }
+ }
+ }
+ }
+ }
+
+ if (!allowRestart) {
+ app.services.clear();
+ }
+ }
+
+ // Make sure we have no more records on the stopping list.
+ int i = mStoppingServices.size();
+ while (i > 0) {
+ i--;
+ ServiceRecord sr = mStoppingServices.get(i);
+ if (sr.app == app) {
+ mStoppingServices.remove(i);
+ if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+ }
+ }
+
+ app.executingServices.clear();
+ }
+
+ ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) {
+ ActivityManager.RunningServiceInfo info =
+ new ActivityManager.RunningServiceInfo();
+ info.service = r.name;
+ if (r.app != null) {
+ info.pid = r.app.pid;
+ }
+ info.uid = r.appInfo.uid;
+ info.process = r.processName;
+ info.foreground = r.isForeground;
+ info.activeSince = r.createTime;
+ info.started = r.startRequested;
+ info.clientCount = r.connections.size();
+ info.crashCount = r.crashCount;
+ info.lastActivityTime = r.lastActivity;
+ if (r.isForeground) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_FOREGROUND;
+ }
+ if (r.startRequested) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED;
+ }
+ if (r.app != null && r.app.pid == ActivityManagerService.MY_PID) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
+ }
+ if (r.app != null && r.app.persistent) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
+ }
+
+ for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
+ for (int i=0; i<connl.size(); i++) {
+ ConnectionRecord conn = connl.get(i);
+ if (conn.clientLabel != 0) {
+ info.clientPackage = conn.binding.client.info.packageName;
+ info.clientLabel = conn.clientLabel;
+ return info;
+ }
+ }
+ }
+ return info;
+ }
+
+ List<ActivityManager.RunningServiceInfo> getRunningServiceInfoLocked(int maxNum,
+ int flags) {
+ ArrayList<ActivityManager.RunningServiceInfo> res
+ = new ArrayList<ActivityManager.RunningServiceInfo>();
+
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ uid) == PackageManager.PERMISSION_GRANTED) {
+ List<UserInfo> users = mAm.getUserManager().getUsers();
+ for (int ui=0; ui<users.size() && res.size() < maxNum; ui++) {
+ final UserInfo user = users.get(ui);
+ if (mServiceMap.getAllServices(user.id).size() > 0) {
+ Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+ user.id).iterator();
+ while (it.hasNext() && res.size() < maxNum) {
+ res.add(makeRunningServiceInfoLocked(it.next()));
+ }
+ }
+ }
+
+ for (int i=0; i<mRestartingServices.size() && res.size() < maxNum; i++) {
+ ServiceRecord r = mRestartingServices.get(i);
+ ActivityManager.RunningServiceInfo info =
+ makeRunningServiceInfoLocked(r);
+ info.restarting = r.nextRestartTime;
+ res.add(info);
+ }
+ } else {
+ int userId = UserHandle.getUserId(uid);
+ if (mServiceMap.getAllServices(userId).size() > 0) {
+ Iterator<ServiceRecord> it
+ = mServiceMap.getAllServices(userId).iterator();
+ while (it.hasNext() && res.size() < maxNum) {
+ res.add(makeRunningServiceInfoLocked(it.next()));
+ }
+ }
+
+ for (int i=0; i<mRestartingServices.size() && res.size() < maxNum; i++) {
+ ServiceRecord r = mRestartingServices.get(i);
+ if (r.userId == userId) {
+ ActivityManager.RunningServiceInfo info =
+ makeRunningServiceInfoLocked(r);
+ info.restarting = r.nextRestartTime;
+ res.add(info);
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ return res;
+ }
+
+ public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) {
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
+ ServiceRecord r = mServiceMap.getServiceByName(name, userId);
+ if (r != null) {
+ for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
+ for (int i=0; i<conn.size(); i++) {
+ if (conn.get(i).clientIntent != null) {
+ return conn.get(i).clientIntent;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ void serviceTimeout(ProcessRecord proc) {
+ String anrMessage = null;
+
+ synchronized(this) {
+ if (proc.executingServices.size() == 0 || proc.thread == null) {
+ return;
+ }
+ long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT;
+ Iterator<ServiceRecord> it = proc.executingServices.iterator();
+ ServiceRecord timeout = null;
+ long nextTime = 0;
+ while (it.hasNext()) {
+ ServiceRecord sr = it.next();
+ if (sr.executingStart < maxTime) {
+ timeout = sr;
+ break;
+ }
+ if (sr.executingStart > nextTime) {
+ nextTime = sr.executingStart;
+ }
+ }
+ if (timeout != null && mAm.mLruProcesses.contains(proc)) {
+ Slog.w(TAG, "Timeout executing service: " + timeout);
+ anrMessage = "Executing service " + timeout.shortName;
+ } else {
+ Message msg = mAm.mHandler.obtainMessage(
+ ActivityManagerService.SERVICE_TIMEOUT_MSG);
+ msg.obj = proc;
+ mAm.mHandler.sendMessageAtTime(msg, nextTime+SERVICE_TIMEOUT);
+ }
+ }
+
+ if (anrMessage != null) {
+ mAm.appNotResponding(proc, null, null, anrMessage);
+ }
+ }
+
+ /**
+ * Prints a list of ServiceRecords (dumpsys activity services)
+ */
+ boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
+ boolean needSep = false;
+
+ ItemMatcher matcher = new ItemMatcher();
+ matcher.build(args, opti);
+
+ pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
+ try {
+ List<UserInfo> users = mAm.getUserManager().getUsers();
+ for (int ui=0; ui<users.size(); ui++) {
+ final UserInfo user = users.get(ui);
+ if (mServiceMap.getAllServices(user.id).size() > 0) {
+ boolean printed = false;
+ long nowReal = SystemClock.elapsedRealtime();
+ Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+ user.id).iterator();
+ needSep = false;
+ while (it.hasNext()) {
+ ServiceRecord r = it.next();
+ if (!matcher.match(r, r.name)) {
+ continue;
+ }
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (!printed) {
+ if (ui > 0) {
+ pw.println();
+ }
+ pw.println(" User " + user.id + " active services:");
+ printed = true;
+ }
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" * ");
+ pw.println(r);
+ if (dumpAll) {
+ r.dump(pw, " ");
+ needSep = true;
+ } else {
+ pw.print(" app=");
+ pw.println(r.app);
+ pw.print(" created=");
+ TimeUtils.formatDuration(r.createTime, nowReal, pw);
+ pw.print(" started=");
+ pw.print(r.startRequested);
+ pw.print(" connections=");
+ pw.println(r.connections.size());
+ if (r.connections.size() > 0) {
+ pw.println(" Connections:");
+ for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
+ for (int i = 0; i < clist.size(); i++) {
+ ConnectionRecord conn = clist.get(i);
+ pw.print(" ");
+ pw.print(conn.binding.intent.intent.getIntent()
+ .toShortString(false, false, false, false));
+ pw.print(" -> ");
+ ProcessRecord proc = conn.binding.client;
+ pw.println(proc != null ? proc.toShortString() : "null");
+ }
+ }
+ }
+ }
+ if (dumpClient && r.app != null && r.app.thread != null) {
+ pw.println(" Client:");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
+ r, args);
+ tp.setBufferPrefix(" ");
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(" Failure while dumping the service: " + e);
+ } catch (RemoteException e) {
+ pw.println(" Got a RemoteException while dumping the service");
+ }
+ needSep = true;
+ }
+ }
+ needSep = printed;
+ }
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Exception in dumpServicesLocked: " + e);
+ }
+
+ if (mPendingServices.size() > 0) {
+ boolean printed = false;
+ for (int i=0; i<mPendingServices.size(); i++) {
+ ServiceRecord r = mPendingServices.get(i);
+ if (!matcher.match(r, r.name)) {
+ continue;
+ }
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println(" ");
+ needSep = true;
+ pw.println(" Pending services:");
+ printed = true;
+ }
+ pw.print(" * Pending "); pw.println(r);
+ r.dump(pw, " ");
+ }
+ needSep = true;
+ }
+
+ if (mRestartingServices.size() > 0) {
+ boolean printed = false;
+ for (int i=0; i<mRestartingServices.size(); i++) {
+ ServiceRecord r = mRestartingServices.get(i);
+ if (!matcher.match(r, r.name)) {
+ continue;
+ }
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println(" ");
+ needSep = true;
+ pw.println(" Restarting services:");
+ printed = true;
+ }
+ pw.print(" * Restarting "); pw.println(r);
+ r.dump(pw, " ");
+ }
+ needSep = true;
+ }
+
+ if (mStoppingServices.size() > 0) {
+ boolean printed = false;
+ for (int i=0; i<mStoppingServices.size(); i++) {
+ ServiceRecord r = mStoppingServices.get(i);
+ if (!matcher.match(r, r.name)) {
+ continue;
+ }
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println(" ");
+ needSep = true;
+ pw.println(" Stopping services:");
+ printed = true;
+ }
+ pw.print(" * Stopping "); pw.println(r);
+ r.dump(pw, " ");
+ }
+ needSep = true;
+ }
+
+ if (dumpAll) {
+ if (mServiceConnections.size() > 0) {
+ boolean printed = false;
+ Iterator<ArrayList<ConnectionRecord>> it
+ = mServiceConnections.values().iterator();
+ while (it.hasNext()) {
+ ArrayList<ConnectionRecord> r = it.next();
+ for (int i=0; i<r.size(); i++) {
+ ConnectionRecord cr = r.get(i);
+ if (!matcher.match(cr.binding.service, cr.binding.service.name)) {
+ continue;
+ }
+ if (dumpPackage != null && (cr.binding.client == null
+ || !dumpPackage.equals(cr.binding.client.info.packageName))) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println(" ");
+ needSep = true;
+ pw.println(" Connection bindings to services:");
+ printed = true;
+ }
+ pw.print(" * "); pw.println(cr);
+ cr.dump(pw, " ");
+ }
+ }
+ needSep = true;
+ }
+ }
+
+ return needSep;
+ }
+
+ /**
+ * There are three ways to call this:
+ * - no service specified: dump all the services
+ * - a flattened component name that matched an existing service was specified as the
+ * first arg: dump that one service
+ * - the first arg isn't the flattened component name of an existing service:
+ * dump all services whose component contains the first arg as a substring
+ */
+ protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll) {
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+
+ List<UserInfo> users = mAm.getUserManager().getUsers();
+ if ("all".equals(name)) {
+ synchronized (this) {
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ services.add(r1);
+ }
+ }
+ }
+ } else {
+ ComponentName componentName = name != null
+ ? ComponentName.unflattenFromString(name) : null;
+ int objectId = 0;
+ if (componentName == null) {
+ // Not a '/' separated full component name; maybe an object ID?
+ try {
+ objectId = Integer.parseInt(name, 16);
+ name = null;
+ componentName = null;
+ } catch (RuntimeException e) {
+ }
+ }
+
+ synchronized (this) {
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ if (componentName != null) {
+ if (r1.name.equals(componentName)) {
+ services.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.name.flattenToString().contains(name)) {
+ services.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
+ services.add(r1);
+ }
+ }
+ }
+ }
+ }
+
+ if (services.size() <= 0) {
+ return false;
+ }
+
+ boolean needSep = false;
+ for (int i=0; i<services.size(); i++) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ dumpService("", fd, pw, services.get(i), args, dumpAll);
+ }
+ return true;
+ }
+
+ /**
+ * Invokes IApplicationThread.dumpService() on the thread of the specified service if
+ * there is a thread associated with the service.
+ */
+ private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw,
+ final ServiceRecord r, String[] args, boolean dumpAll) {
+ String innerPrefix = prefix + " ";
+ synchronized (this) {
+ pw.print(prefix); pw.print("SERVICE ");
+ pw.print(r.shortName); pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(r)));
+ pw.print(" pid=");
+ if (r.app != null) pw.println(r.app.pid);
+ else pw.println("(not running)");
+ if (dumpAll) {
+ r.dump(pw, innerPrefix);
+ }
+ }
+ if (r.app != null && r.app.thread != null) {
+ pw.print(prefix); pw.println(" Client:");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
+ tp.setBufferPrefix(prefix + " ");
+ tp.go(fd);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(prefix + " Failure while dumping the service: " + e);
+ } catch (RemoteException e) {
+ pw.println(prefix + " Got a RemoteException while dumping the service");
+ }
+ }
+ }
+
+}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 73deef0..2b4f8b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -51,7 +51,6 @@ import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.Service;
import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -76,12 +75,12 @@ import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -111,7 +110,8 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.format.Time;
import android.util.EventLog;
@@ -144,7 +144,6 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -242,31 +241,6 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
- // How long we wait for a service to finish executing.
- static final int SERVICE_TIMEOUT = 20*1000;
-
- // How long a service needs to be running until restarting its process
- // is no longer considered to be a relaunch of the service.
- static final int SERVICE_RESTART_DURATION = 5*1000;
-
- // How long a service needs to be running until it will start back at
- // SERVICE_RESTART_DURATION after being killed.
- static final int SERVICE_RESET_RUN_DURATION = 60*1000;
-
- // Multiplying factor to increase restart duration time by, for each time
- // a service is killed before it has run for SERVICE_RESET_RUN_DURATION.
- static final int SERVICE_RESTART_DURATION_FACTOR = 4;
-
- // The minimum amount of time between restarting services that we allow.
- // That is, when multiple services are restarting, we won't allow each
- // to restart less than this amount of time from the last one.
- static final int SERVICE_MIN_RESTART_TIME_BETWEEN = 10*1000;
-
- // Maximum amount of time for there to be no activity on a service before
- // we consider it non-essential and allow its process to go on the
- // LRU background list.
- static final int MAX_SERVICE_INACTIVITY = 30*60*1000;
-
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
@@ -514,6 +488,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ protected BroadcastFilter[] newArray(int size) {
+ return new BroadcastFilter[size];
+ }
+
+ @Override
protected String packageForFilter(BroadcastFilter filter) {
return filter.packageName;
}
@@ -527,35 +506,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
new HashMap<String, ArrayList<Intent>>();
- final ServiceMap mServiceMap = new ServiceMap();
-
- /**
- * All currently bound service connections. Keys are the IBinder of
- * the client's IServiceConnection.
- */
- final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections
- = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
-
- /**
- * List of services that we have been asked to start,
- * but haven't yet been able to. It is used to hold start requests
- * while waiting for their corresponding application thread to get
- * going.
- */
- final ArrayList<ServiceRecord> mPendingServices
- = new ArrayList<ServiceRecord>();
-
- /**
- * List of services that are scheduled to restart following a crash.
- */
- final ArrayList<ServiceRecord> mRestartingServices
- = new ArrayList<ServiceRecord>();
-
- /**
- * List of services that are in the process of being stopped.
- */
- final ArrayList<ServiceRecord> mStoppingServices
- = new ArrayList<ServiceRecord>();
+ final ActiveServices mServices;
/**
* Backup/restore process management
@@ -733,6 +684,18 @@ public final class ActivityManagerService extends ActivityManagerNative
int mLruSeq = 0;
/**
+ * Keep track of the non-hidden/empty process we last found, to help
+ * determine how to distribute hidden/empty processes next time.
+ */
+ int mNumNonHiddenProcs = 0;
+
+ /**
+ * Keep track of the number of hidden procs, to balance oom adj
+ * distribution between those and empty procs.
+ */
+ int mNumHiddenProcs = 0;
+
+ /**
* Keep track of the number of service processes we last found, to
* determine on the next iteration which should be B services.
*/
@@ -827,6 +790,10 @@ public final class ActivityManagerService extends ActivityManagerNative
static ActivityManagerService mSelf;
static ActivityThread mSystemThread;
+ private int mCurrentUserId;
+ private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+ private UserManager mUserManager;
+
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
@@ -1010,10 +977,10 @@ public final class ActivityManagerService extends ActivityManagerNative
mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
nmsg.obj = msg.obj;
- mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT);
+ mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT);
return;
}
- serviceTimeout((ProcessRecord)msg.obj);
+ mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
@@ -1115,7 +1082,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean restart = (msg.arg2 == 1);
String pkg = (String) msg.obj;
forceStopPackageLocked(pkg, uid, restart, false, true, false,
- UserId.getUserId(uid));
+ UserHandle.getUserId(uid));
}
} break;
case FINALIZE_PENDING_INTENT_MSG: {
@@ -1284,7 +1251,8 @@ public final class ActivityManagerService extends ActivityManagerNative
catPw.println();
dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null);
catPw.println();
- dumpServicesLocked(null, catPw, emptyArgs, 0, false, false, null);
+ mServices.dumpServicesLocked(null, catPw, emptyArgs, 0,
+ false, false, null);
catPw.println();
dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
}
@@ -1521,6 +1489,8 @@ public final class ActivityManagerService extends ActivityManagerNative
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
+ mServices = new ActiveServices(this);
+
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
@@ -1857,7 +1827,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (procs == null) return null;
final int N = procs.size();
for (int i = 0; i < N; i++) {
- if (UserId.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i);
+ if (UserHandle.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i);
}
}
ProcessRecord proc = mProcessNames.get(processName, uid);
@@ -1992,7 +1962,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
- app.pid = 0;
+ app.setPid(0);
}
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
@@ -2008,13 +1978,18 @@ public final class ActivityManagerService extends ActivityManagerNative
int uid = app.uid;
int[] gids = null;
+ int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
try {
- gids = mContext.getPackageManager().getPackageGids(
- app.info.packageName);
+ final PackageManager pm = mContext.getPackageManager();
+ gids = pm.getPackageGids(app.info.packageName);
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
+
+ if (Environment.isExternalStorageEmulated()) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
+ }
}
if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
@@ -2053,7 +2028,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
- app.processName, uid, uid, gids, debugFlags,
+ app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, null, null);
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
@@ -2095,7 +2070,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
buf.append("}");
Slog.i(TAG, buf.toString());
- app.pid = startResult.pid;
+ app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
app.removed = false;
synchronized (mPidsSelfLocked) {
@@ -2107,7 +2082,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
} catch (RuntimeException e) {
// XXX do better error recovery.
- app.pid = 0;
+ app.setPid(0);
Slog.e(TAG, "Failure starting process " + app.processName, e);
}
}
@@ -2223,7 +2198,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
void enforceNotIsolatedCaller(String caller) {
- if (UserId.isIsolated(Binder.getCallingUid())) {
+ if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
}
@@ -2351,19 +2326,48 @@ public final class ActivityManagerService extends ActivityManagerNative
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags,
String profileFile, ParcelFileDescriptor profileFd, Bundle options) {
+ return startActivityAsUser(caller, intent, resolvedType, resultTo, resultWho, requestCode,
+ startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId());
+ }
+
+ public final int startActivityAsUser(IApplicationThread caller,
+ Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, int startFlags,
+ String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
- int userId = 0;
- if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
- // Requesting home, set the identity to the current user
- // HACK!
- userId = mCurrentUserId;
+ if (userId != UserHandle.getCallingUserId()) {
+ // Requesting a different user, make sure that they have the permission
+ if (checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
+ == PackageManager.PERMISSION_GRANTED) {
+ // Translate to the current user id, if caller wasn't aware
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUserId;
+ }
+ } else {
+ String msg = "Permission Denial: "
+ + "Request to startActivity as user " + userId
+ + " but is calling from user " + UserHandle.getCallingUserId()
+ + "; this requires "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
} else {
- // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
- // the current user's userId
- if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
- userId = 0;
+ if (intent.getCategories() != null
+ && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
+ // Requesting home, set the identity to the current user
+ // HACK!
+ userId = mCurrentUserId;
} else {
- userId = Binder.getOrigCallingUser();
+ // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
+ // the current user's userId
+ if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
+ userId = 0;
+ } else {
+ userId = Binder.getOrigCallingUser();
+ }
}
}
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
@@ -2456,7 +2460,7 @@ public final class ActivityManagerService extends ActivityManagerNative
AppGlobals.getPackageManager().queryIntentActivities(
intent, r.resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
- UserId.getCallingUserId());
+ UserHandle.getCallingUserId());
// Look for the original activity in the list...
final int N = resolves != null ? resolves.size() : 0;
@@ -2528,7 +2532,6 @@ public final class ActivityManagerService extends ActivityManagerNative
// This is so super not safe, that only the system (or okay root)
// can do it.
- int userId = Binder.getOrigCallingUser();
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.myUid()) {
throw new SecurityException(
@@ -2537,7 +2540,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
- null, null, null, null, options, userId);
+ null, null, null, null, options, UserHandle.getUserId(uid));
return ret;
}
@@ -2561,7 +2564,7 @@ public final class ActivityManagerService extends ActivityManagerNative
"startActivityInPackage only available to the system");
}
int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
- options, UserId.getUserId(uid));
+ options, UserHandle.getUserId(uid));
return ret;
}
@@ -3437,7 +3440,7 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -3511,7 +3514,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -3647,7 +3650,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private void forceStopPackageLocked(final String packageName, int uid) {
- forceStopPackageLocked(packageName, uid, false, false, true, false, UserId.getUserId(uid));
+ forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid));
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
@@ -3657,7 +3660,7 @@ public final class ActivityManagerService extends ActivityManagerNative
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
false, false,
- MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
+ MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
}
private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -3764,27 +3767,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
- if (service.packageName.equals(name)
- && (service.app == null || evenPersistent || !service.app.persistent)) {
- if (!doit) {
- return true;
- }
- didSomething = true;
- Slog.i(TAG, " Force stopping service " + service);
- if (service.app != null) {
- service.app.removed = true;
- }
- service.app = null;
- service.isolatedProc = null;
- services.add(service);
+ if (mServices.forceStopLocked(name, userId, evenPersistent, doit)) {
+ if (!doit) {
+ return true;
}
- }
-
- N = services.size();
- for (i=0; i<N; i++) {
- bringDownServiceLocked(services.get(i), true);
+ didSomething = true;
}
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
@@ -3884,18 +3871,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
- for (int i=0; i<mPendingServices.size(); i++) {
- ServiceRecord sr = mPendingServices.get(i);
- if ((app.uid == sr.appInfo.uid
- && app.processName.equals(sr.processName))
- || sr.isolatedProc == app) {
- Slog.w(TAG, "Forcing bringing down service: " + sr);
- sr.isolatedProc = null;
- mPendingServices.remove(i);
- i--;
- bringDownServiceLocked(sr, true);
- }
- }
+ mServices.processStartTimedOutLocked(app);
EventLog.writeEvent(EventLogTags.AM_KILL, pid,
app.processName, app.setAdj, "start timeout");
Process.killProcessQuiet(pid);
@@ -4095,24 +4071,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// Find any services that should be running in this process...
- if (!badApp && mPendingServices.size() > 0) {
- ServiceRecord sr = null;
+ if (!badApp) {
try {
- for (int i=0; i<mPendingServices.size(); i++) {
- sr = mPendingServices.get(i);
- if (app != sr.isolatedProc && (app.uid != sr.appInfo.uid
- || !processName.equals(sr.processName))) {
- continue;
- }
-
- mPendingServices.remove(i);
- i--;
- realStartServiceLocked(sr, app);
- didSomething = true;
- }
+ didSomething |= mServices.attachApplicationLocked(app, processName);
} catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting service "
- + sr.shortName, e);
badApp = true;
}
}
@@ -4265,12 +4227,14 @@ public final class ActivityManagerService extends ActivityManagerNative
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
- /* TODO: Send this to all users that are to be logged in on startup */
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
- null, null, 0, null, null,
- android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID, Binder.getOrigCallingUser());
+ List<UserInfo> users = getUserManager().getUsers();
+ for (UserInfo user : users) {
+ broadcastIntentLocked(null, null,
+ new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID, user.id);
+ }
}
}
}
@@ -4417,8 +4381,8 @@ public final class ActivityManagerService extends ActivityManagerNative
try {
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
- .getPackageUid(packageName, UserId.getUserId(callingUid));
- if (!UserId.isSameApp(callingUid, uid)) {
+ .getPackageUid(packageName, UserHandle.getUserId(callingUid));
+ if (!UserHandle.isSameApp(callingUid, uid)) {
String msg = "Permission Denial: getIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -4514,8 +4478,8 @@ public final class ActivityManagerService extends ActivityManagerNative
PendingIntentRecord rec = (PendingIntentRecord)sender;
try {
int uid = AppGlobals.getPackageManager()
- .getPackageUid(rec.key.packageName, UserId.getCallingUserId());
- if (!UserId.isSameApp(uid, Binder.getCallingUid())) {
+ .getPackageUid(rec.key.packageName, UserHandle.getCallingUserId());
+ if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
String msg = "Permission Denial: cancelIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -4734,7 +4698,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
+ return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
}
/**
@@ -4744,7 +4708,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int checkCallingPermission(String permission) {
return checkPermission(permission,
Binder.getCallingPid(),
- UserId.getAppId(Binder.getCallingUid()));
+ UserHandle.getAppId(Binder.getCallingUid()));
}
/**
@@ -4875,7 +4839,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pid = tlsIdentity.pid;
}
- uid = UserId.getAppId(uid);
+ uid = UserHandle.getAppId(uid);
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -4921,13 +4885,13 @@ public final class ActivityManagerService extends ActivityManagerNative
String name = uri.getAuthority();
ProviderInfo pi = null;
ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
- UserId.getUserId(callingUid));
+ UserHandle.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
try {
pi = pm.resolveContentProvider(name,
- PackageManager.GET_URI_PERMISSION_PATTERNS, UserId.getUserId(callingUid));
+ PackageManager.GET_URI_PERMISSION_PATTERNS, UserHandle.getUserId(callingUid));
} catch (RemoteException ex) {
}
}
@@ -4939,7 +4903,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int targetUid = lastTargetUid;
if (targetUid < 0 && targetPkg != null) {
try {
- targetUid = pm.getPackageUid(targetPkg, UserId.getUserId(callingUid));
+ targetUid = pm.getPackageUid(targetPkg, UserHandle.getUserId(callingUid));
if (targetUid < 0) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Can't grant URI permission no uid for: " + targetPkg);
@@ -5231,7 +5195,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- int userId = UserId.getUserId(callingUid);
+ int userId = UserHandle.getUserId(callingUid);
ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
if (cpr != null) {
pi = cpr.info;
@@ -5567,13 +5531,28 @@ public final class ActivityManagerService extends ActivityManagerNative
}
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
- int flags) {
+ int flags, int userId) {
final int callingUid = Binder.getCallingUid();
- // If it's the system uid asking, then use the current user id.
- // TODO: Make sure that there aren't any other legitimate calls from the system uid that
- // require the entire list.
- final int callingUserId = callingUid == Process.SYSTEM_UID
- ? mCurrentUserId : UserId.getUserId(callingUid);
+ if (userId != UserHandle.getCallingUserId()) {
+ // Check if the caller is holding permissions for cross-user requests.
+ if (checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingPid(), callingUid, -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: "
+ + "Request to get recent tasks for user " + userId
+ + " but is calling from user " + UserHandle.getUserId(callingUid)
+ + "; this requires "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ } else {
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUserId;
+ }
+ }
+ }
+
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
@@ -5582,7 +5561,7 @@ public final class ActivityManagerService extends ActivityManagerNative
== PackageManager.PERMISSION_GRANTED;
IPackageManager pm = AppGlobals.getPackageManager();
-
+
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
= new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -5590,7 +5569,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N && maxNum > 0; i++) {
TaskRecord tr = mRecentTasks.get(i);
// Only add calling user's recent tasks
- if (tr.userId != callingUserId) continue;
+ if (tr.userId != userId) continue;
// Return the entry if desired by the caller. We always return
// the first entry, because callers always expect this to be the
// foreground app. We may filter others if the caller has
@@ -5618,13 +5597,13 @@ public final class ActivityManagerService extends ActivityManagerNative
// Check whether this activity is currently available.
try {
if (rti.origActivity != null) {
- if (pm.getActivityInfo(rti.origActivity, 0, callingUserId)
+ if (pm.getActivityInfo(rti.origActivity, 0, userId)
== null) {
continue;
}
} else if (rti.baseIntent != null) {
if (pm.queryIntentActivities(rti.baseIntent,
- null, 0, callingUserId) == null) {
+ null, 0, userId) == null) {
continue;
}
}
@@ -5689,29 +5668,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// Find any running services associated with this app.
- ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord sr : mServiceMap.getAllServices(tr.userId)) {
- if (sr.packageName.equals(component.getPackageName())) {
- services.add(sr);
- }
- }
-
- // Take care of any running services associated with the app.
- for (int i=0; i<services.size(); i++) {
- ServiceRecord sr = services.get(i);
- if (sr.startRequested) {
- if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
- Slog.i(TAG, "Stopping service " + sr.shortName + ": remove task");
- stopServiceLocked(sr);
- } else {
- sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
- sr.makeNextStartId(), baseIntent, null));
- if (sr.app != null && sr.app.thread != null) {
- sendServiceArgsLocked(sr, false);
- }
- }
- }
- }
+ mServices.cleanUpRemovedTaskLocked(tr, component, baseIntent);
if (killProcesses) {
// Find any running processes associated with this app.
@@ -6055,15 +6012,26 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
int userId = app.userId;
if (providers != null) {
- final int N = providers.size();
+ int N = providers.size();
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
+ boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags);
+ if (singleton && UserHandle.getUserId(app.uid) != 0) {
+ // This is a singleton provider, but a user besides the
+ // default user is asking to initialize a process it runs
+ // in... well, no, it doesn't actually run in this process,
+ // it runs in the process of the default user. Get rid of it.
+ providers.remove(i);
+ N--;
+ continue;
+ }
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
- cpr = new ContentProviderRecord(this, cpi, app.info, comp);
+ cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
mProviderMap.putProviderByClass(comp, cpr);
}
if (DEBUG_MU)
@@ -6221,7 +6189,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// First check if this content provider has been published...
- int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid());
+ int userId = UserHandle.getUserId(r != null ? r.uid : Binder.getCallingUid());
cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
@@ -6296,6 +6264,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Binder.restoreCallingIdentity(origId);
}
+ boolean singleton;
if (!providerRunning) {
try {
cpi = AppGlobals.getPackageManager().
@@ -6306,7 +6275,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (cpi == null) {
return null;
}
- if (isSingleton(cpi.processName, cpi.applicationInfo)) {
+ singleton = isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags);
+ if (singleton) {
userId = 0;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
@@ -6341,7 +6312,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
ai = getAppInfoForUser(ai, userId);
- cpr = new ContentProviderRecord(this, cpi, ai, comp);
+ cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
@@ -6766,7 +6737,7 @@ public final class ActivityManagerService extends ActivityManagerNative
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
int uid = info.uid;
if (isolated) {
- int userId = UserId.getUserId(uid);
+ int userId = UserHandle.getUserId(uid);
int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
uid = 0;
while (true) {
@@ -6774,7 +6745,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|| mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
}
- uid = UserId.getUid(userId, mNextIsolatedProcessUid);
+ uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
mNextIsolatedProcessUid++;
if (mIsolatedProcesses.indexOfKey(uid) < 0) {
// No process for this uid, use it.
@@ -6812,7 +6783,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- info.packageName, false, UserId.getUserId(app.uid));
+ info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
@@ -7219,7 +7190,7 @@ public final class ActivityManagerService extends ActivityManagerNative
lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
- lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
+ lp.gravity = Gravity.BOTTOM | Gravity.START;
lp.format = v.getBackground().getOpacity();
lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -8423,11 +8394,19 @@ public final class ActivityManagerService extends ActivityManagerNative
// assume our apps are happy - lazy create the list
List<ActivityManager.ProcessErrorStateInfo> errList = null;
+ final boolean allUsers = ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
+
synchronized (this) {
// iterate across all processes
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
+ if (!allUsers && app.userId != userId) {
+ continue;
+ }
if ((app.thread != null) && (app.crashing || app.notResponding)) {
// This one's in trouble, so we'll generate a report for it
// crashes are higher priority (in case there's a crash *and* an anr)
@@ -8491,6 +8470,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.persistent) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
}
+ if (app.hasActivities) {
+ outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
+ }
outInfo.lastTrimLevel = app.trimMemoryLevel;
int adj = app.curAdj;
outInfo.importance = oomAdjToImportance(adj, outInfo);
@@ -8501,10 +8483,17 @@ public final class ActivityManagerService extends ActivityManagerNative
enforceNotIsolatedCaller("getRunningAppProcesses");
// Lazy instantiation of list
List<ActivityManager.RunningAppProcessInfo> runList = null;
+ final boolean allUsers = ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
synchronized (this) {
// Iterate across all processes
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
+ if (!allUsers && app.userId != userId) {
+ continue;
+ }
if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
@@ -8550,7 +8539,7 @@ public final class ActivityManagerService extends ActivityManagerNative
IPackageManager pm = AppGlobals.getPackageManager();
for (String pkg : extList) {
try {
- ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserId.getCallingUserId());
+ ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserHandle.getCallingUserId());
if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
retList.add(info);
}
@@ -8723,7 +8712,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
- if (!dumpService(fd, pw, name, newArgs, 0, dumpAll)) {
+ if (!mServices.dumpService(fd, pw, name, newArgs, 0, dumpAll)) {
pw.println("No services match: " + name);
pw.println("Use -h for help.");
}
@@ -8744,7 +8733,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
} else if ("services".equals(cmd) || "s".equals(cmd)) {
synchronized (this) {
- dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null);
+ mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null);
}
} else {
// Dumping a single activity?
@@ -8783,7 +8772,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+ needSep = mServices.dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
if (needSep) {
pw.println(" ");
}
@@ -9134,7 +9123,9 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep);
pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity);
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
- pw.println(" mNumServiceProcs=" + mNumServiceProcs
+ pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs
+ + " mNumHiddenProcs=" + mNumHiddenProcs
+ + " mNumServiceProcs=" + mNumServiceProcs
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
}
@@ -9214,120 +9205,6 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* There are three ways to call this:
- * - no service specified: dump all the services
- * - a flattened component name that matched an existing service was specified as the
- * first arg: dump that one service
- * - the first arg isn't the flattened component name of an existing service:
- * dump all services whose component contains the first arg as a substring
- */
- protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args,
- int opti, boolean dumpAll) {
- ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
-
- if ("all".equals(name)) {
- synchronized (this) {
- try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
- services.add(r1);
- }
- }
- } catch (RemoteException re) {
- }
- }
- } else {
- ComponentName componentName = name != null
- ? ComponentName.unflattenFromString(name) : null;
- int objectId = 0;
- if (componentName == null) {
- // Not a '/' separated full component name; maybe an object ID?
- try {
- objectId = Integer.parseInt(name, 16);
- name = null;
- componentName = null;
- } catch (RuntimeException e) {
- }
- }
-
- synchronized (this) {
- try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
- if (componentName != null) {
- if (r1.name.equals(componentName)) {
- services.add(r1);
- }
- } else if (name != null) {
- if (r1.name.flattenToString().contains(name)) {
- services.add(r1);
- }
- } else if (System.identityHashCode(r1) == objectId) {
- services.add(r1);
- }
- }
- }
- } catch (RemoteException re) {
- }
- }
- }
-
- if (services.size() <= 0) {
- return false;
- }
-
- boolean needSep = false;
- for (int i=0; i<services.size(); i++) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- dumpService("", fd, pw, services.get(i), args, dumpAll);
- }
- return true;
- }
-
- /**
- * Invokes IApplicationThread.dumpService() on the thread of the specified service if
- * there is a thread associated with the service.
- */
- private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw,
- final ServiceRecord r, String[] args, boolean dumpAll) {
- String innerPrefix = prefix + " ";
- synchronized (this) {
- pw.print(prefix); pw.print("SERVICE ");
- pw.print(r.shortName); pw.print(" ");
- pw.print(Integer.toHexString(System.identityHashCode(r)));
- pw.print(" pid=");
- if (r.app != null) pw.println(r.app.pid);
- else pw.println("(not running)");
- if (dumpAll) {
- r.dump(pw, innerPrefix);
- }
- }
- if (r.app != null && r.app.thread != null) {
- pw.print(prefix); pw.println(" Client:");
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
- tp.setBufferPrefix(prefix + " ");
- tp.go(fd);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(prefix + " Failure while dumping the service: " + e);
- } catch (RemoteException e) {
- pw.println(prefix + " Got a RemoteException while dumping the service");
- }
- }
- }
-
- /**
- * There are three ways to call this:
* - no provider specified: dump all the providers
* - a flattened component name that matched an existing provider was specified as the
* first arg: dump that one provider
@@ -9608,199 +9485,6 @@ public final class ActivityManagerService extends ActivityManagerNative
return needSep;
}
- /**
- * Prints a list of ServiceRecords (dumpsys activity services)
- */
- boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
- boolean needSep = false;
-
- ItemMatcher matcher = new ItemMatcher();
- matcher.build(args, opti);
-
- pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
- try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- if (mServiceMap.getAllServices(user.id).size() > 0) {
- boolean printed = false;
- long nowReal = SystemClock.elapsedRealtime();
- Iterator<ServiceRecord> it = mServiceMap.getAllServices(
- user.id).iterator();
- needSep = false;
- while (it.hasNext()) {
- ServiceRecord r = it.next();
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
- continue;
- }
- if (!printed) {
- pw.println(" Active services:");
- printed = true;
- }
- if (needSep) {
- pw.println();
- }
- pw.print(" * ");
- pw.println(r);
- if (dumpAll) {
- r.dump(pw, " ");
- needSep = true;
- } else {
- pw.print(" app=");
- pw.println(r.app);
- pw.print(" created=");
- TimeUtils.formatDuration(r.createTime, nowReal, pw);
- pw.print(" started=");
- pw.print(r.startRequested);
- pw.print(" connections=");
- pw.println(r.connections.size());
- if (r.connections.size() > 0) {
- pw.println(" Connections:");
- for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
- for (int i = 0; i < clist.size(); i++) {
- ConnectionRecord conn = clist.get(i);
- pw.print(" ");
- pw.print(conn.binding.intent.intent.getIntent()
- .toShortString(false, false, false, false));
- pw.print(" -> ");
- ProcessRecord proc = conn.binding.client;
- pw.println(proc != null ? proc.toShortString() : "null");
- }
- }
- }
- }
- if (dumpClient && r.app != null && r.app.thread != null) {
- pw.println(" Client:");
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
- r, args);
- tp.setBufferPrefix(" ");
- // Short timeout, since blocking here can
- // deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(" Failure while dumping the service: " + e);
- } catch (RemoteException e) {
- pw.println(" Got a RemoteException while dumping the service");
- }
- needSep = true;
- }
- }
- needSep = printed;
- }
- }
- } catch (RemoteException re) {
-
- }
-
- if (mPendingServices.size() > 0) {
- boolean printed = false;
- for (int i=0; i<mPendingServices.size(); i++) {
- ServiceRecord r = mPendingServices.get(i);
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Pending services:");
- printed = true;
- }
- pw.print(" * Pending "); pw.println(r);
- r.dump(pw, " ");
- }
- needSep = true;
- }
-
- if (mRestartingServices.size() > 0) {
- boolean printed = false;
- for (int i=0; i<mRestartingServices.size(); i++) {
- ServiceRecord r = mRestartingServices.get(i);
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Restarting services:");
- printed = true;
- }
- pw.print(" * Restarting "); pw.println(r);
- r.dump(pw, " ");
- }
- needSep = true;
- }
-
- if (mStoppingServices.size() > 0) {
- boolean printed = false;
- for (int i=0; i<mStoppingServices.size(); i++) {
- ServiceRecord r = mStoppingServices.get(i);
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Stopping services:");
- printed = true;
- }
- pw.print(" * Stopping "); pw.println(r);
- r.dump(pw, " ");
- }
- needSep = true;
- }
-
- if (dumpAll) {
- if (mServiceConnections.size() > 0) {
- boolean printed = false;
- Iterator<ArrayList<ConnectionRecord>> it
- = mServiceConnections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> r = it.next();
- for (int i=0; i<r.size(); i++) {
- ConnectionRecord cr = r.get(i);
- if (!matcher.match(cr.binding.service, cr.binding.service.name)) {
- continue;
- }
- if (dumpPackage != null && (cr.binding.client == null
- || !dumpPackage.equals(cr.binding.client.info.packageName))) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Connection bindings to services:");
- printed = true;
- }
- pw.print(" * "); pw.println(cr);
- cr.dump(pw, " ");
- }
- }
- needSep = true;
- }
- }
-
- return needSep;
- }
-
boolean dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = true;
@@ -10106,6 +9790,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.print(" ");
pw.print("oom: max="); pw.print(r.maxAdj);
pw.print(" hidden="); pw.print(r.hiddenAdj);
+ pw.print(" empty="); pw.print(r.emptyAdj);
pw.print(" curRaw="); pw.print(r.curRawAdj);
pw.print(" setRaw="); pw.print(r.setRawAdj);
pw.print(" cur="); pw.print(r.curAdj);
@@ -10594,125 +10279,6 @@ public final class ActivityManagerService extends ActivityManagerNative
return false;
}
- private final void killServicesLocked(ProcessRecord app,
- boolean allowRestart) {
- // Report disconnected services.
- if (false) {
- // XXX we are letting the client link to the service for
- // death notifications.
- if (app.services.size() > 0) {
- Iterator<ServiceRecord> it = app.services.iterator();
- while (it.hasNext()) {
- ServiceRecord r = it.next();
- if (r.connections.size() > 0) {
- Iterator<ArrayList<ConnectionRecord>> jt
- = r.connections.values().iterator();
- while (jt.hasNext()) {
- ArrayList<ConnectionRecord> cl = jt.next();
- for (int i=0; i<cl.size(); i++) {
- ConnectionRecord c = cl.get(i);
- if (c.binding.client != app) {
- try {
- //c.conn.connected(r.className, null);
- } catch (Exception e) {
- // todo: this should be asynchronous!
- Slog.w(TAG, "Exception thrown disconnected servce "
- + r.shortName
- + " from app " + app.processName, e);
- }
- }
- }
- }
- }
- }
- }
- }
-
- // Clean up any connections this application has to other services.
- if (app.connections.size() > 0) {
- Iterator<ConnectionRecord> it = app.connections.iterator();
- while (it.hasNext()) {
- ConnectionRecord r = it.next();
- removeConnectionLocked(r, app, null);
- }
- }
- app.connections.clear();
-
- if (app.services.size() != 0) {
- // Any services running in the application need to be placed
- // back in the pending list.
- Iterator<ServiceRecord> it = app.services.iterator();
- while (it.hasNext()) {
- ServiceRecord sr = it.next();
- synchronized (sr.stats.getBatteryStats()) {
- sr.stats.stopLaunchedLocked();
- }
- sr.app = null;
- sr.isolatedProc = null;
- sr.executeNesting = 0;
- if (mStoppingServices.remove(sr)) {
- if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
- }
-
- boolean hasClients = sr.bindings.size() > 0;
- if (hasClients) {
- Iterator<IntentBindRecord> bindings
- = sr.bindings.values().iterator();
- while (bindings.hasNext()) {
- IntentBindRecord b = bindings.next();
- if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
- + ": shouldUnbind=" + b.hasBound);
- b.binder = null;
- b.requested = b.received = b.hasBound = false;
- }
- }
-
- if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
- &ApplicationInfo.FLAG_PERSISTENT) == 0) {
- Slog.w(TAG, "Service crashed " + sr.crashCount
- + " times, stopping: " + sr);
- EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
- sr.crashCount, sr.shortName, app.pid);
- bringDownServiceLocked(sr, true);
- } else if (!allowRestart) {
- bringDownServiceLocked(sr, true);
- } else {
- boolean canceled = scheduleServiceRestartLocked(sr, true);
-
- // Should the service remain running? Note that in the
- // extreme case of so many attempts to deliver a command
- // that it failed we also will stop it here.
- if (sr.startRequested && (sr.stopIfKilled || canceled)) {
- if (sr.pendingStarts.size() == 0) {
- sr.startRequested = false;
- if (!hasClients) {
- // Whoops, no reason to restart!
- bringDownServiceLocked(sr, true);
- }
- }
- }
- }
- }
-
- if (!allowRestart) {
- app.services.clear();
- }
- }
-
- // Make sure we have no more records on the stopping list.
- int i = mStoppingServices.size();
- while (i > 0) {
- i--;
- ServiceRecord sr = mStoppingServices.get(i);
- if (sr.app == app) {
- mStoppingServices.remove(i);
- if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
- }
- }
-
- app.executingServices.clear();
- }
-
private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr, boolean always) {
final boolean inLaunching = mLaunchingProviders.contains(cpr);
@@ -10722,10 +10288,10 @@ public final class ActivityManagerService extends ActivityManagerNative
cpr.launchingApp = null;
cpr.notifyAll();
}
- mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
+ mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
+ mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
}
}
@@ -10810,7 +10376,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.hasShownUi = false;
app.hasAboveClient = false;
- killServicesLocked(app, allowRestart);
+ mServices.killServicesLocked(app, allowRestart);
boolean restart = false;
@@ -10972,806 +10538,23 @@ public final class ActivityManagerService extends ActivityManagerNative
// SERVICES
// =========================================================
- ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) {
- ActivityManager.RunningServiceInfo info =
- new ActivityManager.RunningServiceInfo();
- info.service = r.name;
- if (r.app != null) {
- info.pid = r.app.pid;
- }
- info.uid = r.appInfo.uid;
- info.process = r.processName;
- info.foreground = r.isForeground;
- info.activeSince = r.createTime;
- info.started = r.startRequested;
- info.clientCount = r.connections.size();
- info.crashCount = r.crashCount;
- info.lastActivityTime = r.lastActivity;
- if (r.isForeground) {
- info.flags |= ActivityManager.RunningServiceInfo.FLAG_FOREGROUND;
- }
- if (r.startRequested) {
- info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED;
- }
- if (r.app != null && r.app.pid == MY_PID) {
- info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
- }
- if (r.app != null && r.app.persistent) {
- info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
- }
-
- for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
- for (int i=0; i<connl.size(); i++) {
- ConnectionRecord conn = connl.get(i);
- if (conn.clientLabel != 0) {
- info.clientPackage = conn.binding.client.info.packageName;
- info.clientLabel = conn.clientLabel;
- return info;
- }
- }
- }
- return info;
- }
-
public List<ActivityManager.RunningServiceInfo> getServices(int maxNum,
int flags) {
enforceNotIsolatedCaller("getServices");
synchronized (this) {
- ArrayList<ActivityManager.RunningServiceInfo> res
- = new ArrayList<ActivityManager.RunningServiceInfo>();
-
- int userId = UserId.getUserId(Binder.getCallingUid());
- if (mServiceMap.getAllServices(userId).size() > 0) {
- Iterator<ServiceRecord> it
- = mServiceMap.getAllServices(userId).iterator();
- while (it.hasNext() && res.size() < maxNum) {
- res.add(makeRunningServiceInfoLocked(it.next()));
- }
- }
-
- for (int i=0; i<mRestartingServices.size() && res.size() < maxNum; i++) {
- ServiceRecord r = mRestartingServices.get(i);
- ActivityManager.RunningServiceInfo info =
- makeRunningServiceInfoLocked(r);
- info.restarting = r.nextRestartTime;
- res.add(info);
- }
-
- return res;
+ return mServices.getRunningServiceInfoLocked(maxNum, flags);
}
}
public PendingIntent getRunningServiceControlPanel(ComponentName name) {
enforceNotIsolatedCaller("getRunningServiceControlPanel");
synchronized (this) {
- int userId = UserId.getUserId(Binder.getCallingUid());
- ServiceRecord r = mServiceMap.getServiceByName(name, userId);
- if (r != null) {
- for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
- for (int i=0; i<conn.size(); i++) {
- if (conn.get(i).clientIntent != null) {
- return conn.get(i).clientIntent;
- }
- }
- }
- }
+ return mServices.getRunningServiceControlPanelLocked(name);
}
- return null;
}
- private final ServiceRecord findServiceLocked(ComponentName name,
- IBinder token) {
- ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
- return r == token ? r : null;
- }
-
- private final class ServiceLookupResult {
- final ServiceRecord record;
- final String permission;
-
- ServiceLookupResult(ServiceRecord _record, String _permission) {
- record = _record;
- permission = _permission;
- }
- };
-
- private ServiceLookupResult findServiceLocked(Intent service,
- String resolvedType, int userId) {
- ServiceRecord r = null;
- if (service.getComponent() != null) {
- r = mServiceMap.getServiceByName(service.getComponent(), userId);
- }
- if (r == null) {
- Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServiceMap.getServiceByIntent(filter, userId);
- }
-
- if (r == null) {
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveService(
- service, resolvedType, 0, userId);
- ServiceInfo sInfo =
- rInfo != null ? rInfo.serviceInfo : null;
- if (sInfo == null) {
- return null;
- }
-
- ComponentName name = new ComponentName(
- sInfo.applicationInfo.packageName, sInfo.name);
- r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
- } catch (RemoteException ex) {
- // pm is in same process, this will never happen.
- }
- }
- if (r != null) {
- int callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- if (checkComponentPermission(r.permission,
- callingPid, callingUid, r.appInfo.uid, r.exported)
- != PackageManager.PERMISSION_GRANTED) {
- if (!r.exported) {
- Slog.w(TAG, "Permission Denial: Accessing service " + r.name
- + " from pid=" + callingPid
- + ", uid=" + callingUid
- + " that is not exported from uid " + r.appInfo.uid);
- return new ServiceLookupResult(null, "not exported from uid "
- + r.appInfo.uid);
- }
- Slog.w(TAG, "Permission Denial: Accessing service " + r.name
- + " from pid=" + callingPid
- + ", uid=" + callingUid
- + " requires " + r.permission);
- return new ServiceLookupResult(null, r.permission);
- }
- return new ServiceLookupResult(r, null);
- }
- return null;
- }
-
- private class ServiceRestarter implements Runnable {
- private ServiceRecord mService;
-
- void setService(ServiceRecord service) {
- mService = service;
- }
-
- public void run() {
- synchronized(ActivityManagerService.this) {
- performServiceRestartLocked(mService);
- }
- }
- }
-
- private ServiceLookupResult retrieveServiceLocked(Intent service,
- String resolvedType, int callingPid, int callingUid, int userId) {
- ServiceRecord r = null;
- if (DEBUG_SERVICE)
- Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
- + " callingUid=" + callingUid);
-
- if (service.getComponent() != null) {
- r = mServiceMap.getServiceByName(service.getComponent(), userId);
- }
- if (r == null) {
- Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServiceMap.getServiceByIntent(filter, userId);
- }
- if (r == null) {
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveService(
- service, resolvedType, STOCK_PM_FLAGS, userId);
- ServiceInfo sInfo =
- rInfo != null ? rInfo.serviceInfo : null;
- if (sInfo == null) {
- Slog.w(TAG, "Unable to start service " + service +
- ": not found");
- return null;
- }
- if (userId > 0) {
- if (isSingleton(sInfo.processName, sInfo.applicationInfo)) {
- userId = 0;
- }
- sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo, userId);
- }
- ComponentName name = new ComponentName(
- sInfo.applicationInfo.packageName, sInfo.name);
- r = mServiceMap.getServiceByName(name, userId);
- if (r == null) {
- Intent.FilterComparison filter = new Intent.FilterComparison(
- service.cloneFilter());
- ServiceRestarter res = new ServiceRestarter();
- BatteryStatsImpl.Uid.Pkg.Serv ss = null;
- BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
- synchronized (stats) {
- ss = stats.getServiceStatsLocked(
- sInfo.applicationInfo.uid, sInfo.packageName,
- sInfo.name);
- }
- r = new ServiceRecord(this, ss, name, filter, sInfo, res);
- res.setService(r);
- mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
- mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
-
- // Make sure this component isn't in the pending list.
- int N = mPendingServices.size();
- for (int i=0; i<N; i++) {
- ServiceRecord pr = mPendingServices.get(i);
- if (pr.name.equals(name)) {
- mPendingServices.remove(i);
- i--;
- N--;
- }
- }
- }
- } catch (RemoteException ex) {
- // pm is in same process, this will never happen.
- }
- }
- if (r != null) {
- if (checkComponentPermission(r.permission,
- callingPid, callingUid, r.appInfo.uid, r.exported)
- != PackageManager.PERMISSION_GRANTED) {
- if (!r.exported) {
- Slog.w(TAG, "Permission Denial: Accessing service " + r.name
- + " from pid=" + callingPid
- + ", uid=" + callingUid
- + " that is not exported from uid " + r.appInfo.uid);
- return new ServiceLookupResult(null, "not exported from uid "
- + r.appInfo.uid);
- }
- Slog.w(TAG, "Permission Denial: Accessing service " + r.name
- + " from pid=" + callingPid
- + ", uid=" + callingUid
- + " requires " + r.permission);
- return new ServiceLookupResult(null, r.permission);
- }
- return new ServiceLookupResult(r, null);
- }
- return null;
- }
-
- private final void bumpServiceExecutingLocked(ServiceRecord r, String why) {
- if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING "
- + why + " of " + r + " in app " + r.app);
- else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING "
- + why + " of " + r.shortName);
- long now = SystemClock.uptimeMillis();
- if (r.executeNesting == 0 && r.app != null) {
- if (r.app.executingServices.size() == 0) {
- Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
- msg.obj = r.app;
- mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
- }
- r.app.executingServices.add(r);
- }
- r.executeNesting++;
- r.executingStart = now;
- }
-
- private final void sendServiceArgsLocked(ServiceRecord r,
- boolean oomAdjusted) {
- final int N = r.pendingStarts.size();
- if (N == 0) {
- return;
- }
-
- while (r.pendingStarts.size() > 0) {
- try {
- ServiceRecord.StartItem si = r.pendingStarts.remove(0);
- if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: "
- + r + " " + r.intent + " args=" + si.intent);
- if (si.intent == null && N > 1) {
- // If somehow we got a dummy null intent in the middle,
- // then skip it. DO NOT skip a null intent when it is
- // the only one in the list -- this is to support the
- // onStartCommand(null) case.
- continue;
- }
- si.deliveredTime = SystemClock.uptimeMillis();
- r.deliveredStarts.add(si);
- si.deliveryCount++;
- if (si.neededGrants != null) {
- grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
- si.getUriPermissionsLocked());
- }
- bumpServiceExecutingLocked(r, "start");
- if (!oomAdjusted) {
- oomAdjusted = true;
- updateOomAdjLocked(r.app);
- }
- int flags = 0;
- if (si.deliveryCount > 1) {
- flags |= Service.START_FLAG_RETRY;
- }
- if (si.doneExecutingCount > 0) {
- flags |= Service.START_FLAG_REDELIVERY;
- }
- r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
- } catch (RemoteException e) {
- // Remote process gone... we'll let the normal cleanup take
- // care of this.
- if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r);
- break;
- } catch (Exception e) {
- Slog.w(TAG, "Unexpected exception", e);
- break;
- }
- }
- }
-
- private final boolean requestServiceBindingLocked(ServiceRecord r,
- IntentBindRecord i, boolean rebind) {
- if (r.app == null || r.app.thread == null) {
- // If service is not currently running, can't yet bind.
- return false;
- }
- if ((!i.requested || rebind) && i.apps.size() > 0) {
- try {
- bumpServiceExecutingLocked(r, "bind");
- r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
- if (!rebind) {
- i.requested = true;
- }
- i.hasBound = true;
- i.doRebind = false;
- } catch (RemoteException e) {
- if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
- return false;
- }
- }
- return true;
- }
-
- private final void requestServiceBindingsLocked(ServiceRecord r) {
- Iterator<IntentBindRecord> bindings = r.bindings.values().iterator();
- while (bindings.hasNext()) {
- IntentBindRecord i = bindings.next();
- if (!requestServiceBindingLocked(r, i, false)) {
- break;
- }
- }
- }
-
- private final void realStartServiceLocked(ServiceRecord r,
- ProcessRecord app) throws RemoteException {
- if (app.thread == null) {
- throw new RemoteException();
- }
- if (DEBUG_MU)
- Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
- + ", ProcessRecord.uid = " + app.uid);
- r.app = app;
- r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
-
- app.services.add(r);
- bumpServiceExecutingLocked(r, "create");
- updateLruProcessLocked(app, true, true);
-
- boolean created = false;
- try {
- mStringBuilder.setLength(0);
- r.intent.getIntent().toShortString(mStringBuilder, true, false, true, false);
- EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
- System.identityHashCode(r), r.shortName,
- mStringBuilder.toString(), r.app.pid);
- synchronized (r.stats.getBatteryStats()) {
- r.stats.startLaunchedLocked();
- }
- ensurePackageDexOpt(r.serviceInfo.packageName);
- app.thread.scheduleCreateService(r, r.serviceInfo,
- compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
- r.postNotification();
- created = true;
- } finally {
- if (!created) {
- app.services.remove(r);
- scheduleServiceRestartLocked(r, false);
- }
- }
-
- requestServiceBindingsLocked(r);
-
- // If the service is in the started state, and there are no
- // pending arguments, then fake up one so its onStartCommand() will
- // be called.
- if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
- r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
- null, null));
- }
-
- sendServiceArgsLocked(r, true);
- }
-
- private final boolean scheduleServiceRestartLocked(ServiceRecord r,
- boolean allowCancel) {
- boolean canceled = false;
-
- final long now = SystemClock.uptimeMillis();
- long minDuration = SERVICE_RESTART_DURATION;
- long resetTime = SERVICE_RESET_RUN_DURATION;
-
- if ((r.serviceInfo.applicationInfo.flags
- &ApplicationInfo.FLAG_PERSISTENT) != 0) {
- minDuration /= 4;
- }
-
- // Any delivered but not yet finished starts should be put back
- // on the pending list.
- final int N = r.deliveredStarts.size();
- if (N > 0) {
- for (int i=N-1; i>=0; i--) {
- ServiceRecord.StartItem si = r.deliveredStarts.get(i);
- si.removeUriPermissionsLocked();
- if (si.intent == null) {
- // We'll generate this again if needed.
- } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
- && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
- r.pendingStarts.add(0, si);
- long dur = SystemClock.uptimeMillis() - si.deliveredTime;
- dur *= 2;
- if (minDuration < dur) minDuration = dur;
- if (resetTime < dur) resetTime = dur;
- } else {
- Slog.w(TAG, "Canceling start item " + si.intent + " in service "
- + r.name);
- canceled = true;
- }
- }
- r.deliveredStarts.clear();
- }
-
- r.totalRestartCount++;
- if (r.restartDelay == 0) {
- r.restartCount++;
- r.restartDelay = minDuration;
- } else {
- // If it has been a "reasonably long time" since the service
- // was started, then reset our restart duration back to
- // the beginning, so we don't infinitely increase the duration
- // on a service that just occasionally gets killed (which is
- // a normal case, due to process being killed to reclaim memory).
- if (now > (r.restartTime+resetTime)) {
- r.restartCount = 1;
- r.restartDelay = minDuration;
- } else {
- if ((r.serviceInfo.applicationInfo.flags
- &ApplicationInfo.FLAG_PERSISTENT) != 0) {
- // Services in peristent processes will restart much more
- // quickly, since they are pretty important. (Think SystemUI).
- r.restartDelay += minDuration/2;
- } else {
- r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
- if (r.restartDelay < minDuration) {
- r.restartDelay = minDuration;
- }
- }
- }
- }
-
- r.nextRestartTime = now + r.restartDelay;
-
- // Make sure that we don't end up restarting a bunch of services
- // all at the same time.
- boolean repeat;
- do {
- repeat = false;
- for (int i=mRestartingServices.size()-1; i>=0; i--) {
- ServiceRecord r2 = mRestartingServices.get(i);
- if (r2 != r && r.nextRestartTime
- >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
- && r.nextRestartTime
- < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
- r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
- r.restartDelay = r.nextRestartTime - now;
- repeat = true;
- break;
- }
- }
- } while (repeat);
-
- if (!mRestartingServices.contains(r)) {
- mRestartingServices.add(r);
- }
-
- r.cancelNotification();
-
- mHandler.removeCallbacks(r.restarter);
- mHandler.postAtTime(r.restarter, r.nextRestartTime);
- r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
- Slog.w(TAG, "Scheduling restart of crashed service "
- + r.shortName + " in " + r.restartDelay + "ms");
- EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
- r.shortName, r.restartDelay);
-
- return canceled;
- }
-
- final void performServiceRestartLocked(ServiceRecord r) {
- if (!mRestartingServices.contains(r)) {
- return;
- }
- bringUpServiceLocked(r, r.intent.getIntent().getFlags(), true);
- }
-
- private final boolean unscheduleServiceRestartLocked(ServiceRecord r) {
- if (r.restartDelay == 0) {
- return false;
- }
- r.resetRestartCounter();
- mRestartingServices.remove(r);
- mHandler.removeCallbacks(r.restarter);
- return true;
- }
-
- private final boolean bringUpServiceLocked(ServiceRecord r,
- int intentFlags, boolean whileRestarting) {
- //Slog.i(TAG, "Bring up service:");
- //r.dump(" ");
-
- if (r.app != null && r.app.thread != null) {
- sendServiceArgsLocked(r, false);
- return true;
- }
-
- if (!whileRestarting && r.restartDelay > 0) {
- // If waiting for a restart, then do nothing.
- return true;
- }
-
- if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
-
- // We are now bringing the service up, so no longer in the
- // restarting state.
- mRestartingServices.remove(r);
-
- // Service is now being launched, its package can't be stopped.
- try {
- AppGlobals.getPackageManager().setPackageStoppedState(
- r.packageName, false, r.userId);
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + r.packageName + ": " + e);
- }
-
- final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
- final String appName = r.processName;
- ProcessRecord app;
-
- if (!isolated) {
- app = getProcessRecordLocked(appName, r.appInfo.uid);
- if (DEBUG_MU)
- Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
- if (app != null && app.thread != null) {
- try {
- app.addPackage(r.appInfo.packageName);
- realStartServiceLocked(r, app);
- return true;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when starting service " + r.shortName, e);
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
- } else {
- // If this service runs in an isolated process, then each time
- // we call startProcessLocked() we will get a new isolated
- // process, starting another process if we are currently waiting
- // for a previous process to come up. To deal with this, we store
- // in the service any current isolated process it is running in or
- // waiting to have come up.
- app = r.isolatedProc;
- }
-
- // Not running -- get it started, and enqueue this service record
- // to be executed when the app comes up.
- if (app == null) {
- if ((app=startProcessLocked(appName, r.appInfo, true, intentFlags,
- "service", r.name, false, isolated)) == null) {
- Slog.w(TAG, "Unable to launch app "
- + r.appInfo.packageName + "/"
- + r.appInfo.uid + " for service "
- + r.intent.getIntent() + ": process is bad");
- bringDownServiceLocked(r, true);
- return false;
- }
- if (isolated) {
- r.isolatedProc = app;
- }
- }
-
- if (!mPendingServices.contains(r)) {
- mPendingServices.add(r);
- }
-
- return true;
- }
-
- private final void bringDownServiceLocked(ServiceRecord r, boolean force) {
- //Slog.i(TAG, "Bring down service:");
- //r.dump(" ");
-
- // Does it still need to run?
- if (!force && r.startRequested) {
- return;
- }
- if (r.connections.size() > 0) {
- if (!force) {
- // XXX should probably keep a count of the number of auto-create
- // connections directly in the service.
- Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> cr = it.next();
- for (int i=0; i<cr.size(); i++) {
- if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
- return;
- }
- }
- }
- }
-
- // Report to all of the connections that the service is no longer
- // available.
- Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> c = it.next();
- for (int i=0; i<c.size(); i++) {
- ConnectionRecord cr = c.get(i);
- // There is still a connection to the service that is
- // being brought down. Mark it as dead.
- cr.serviceDead = true;
- try {
- cr.conn.connected(r.name, null);
- } catch (Exception e) {
- Slog.w(TAG, "Failure disconnecting service " + r.name +
- " to connection " + c.get(i).conn.asBinder() +
- " (in " + c.get(i).binding.client.processName + ")", e);
- }
- }
- }
- }
-
- // Tell the service that it has been unbound.
- if (r.bindings.size() > 0 && r.app != null && r.app.thread != null) {
- Iterator<IntentBindRecord> it = r.bindings.values().iterator();
- while (it.hasNext()) {
- IntentBindRecord ibr = it.next();
- if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr
- + ": hasBound=" + ibr.hasBound);
- if (r.app != null && r.app.thread != null && ibr.hasBound) {
- try {
- bumpServiceExecutingLocked(r, "bring down unbind");
- updateOomAdjLocked(r.app);
- ibr.hasBound = false;
- r.app.thread.scheduleUnbindService(r,
- ibr.intent.getIntent());
- } catch (Exception e) {
- Slog.w(TAG, "Exception when unbinding service "
- + r.shortName, e);
- serviceDoneExecutingLocked(r, true);
- }
- }
- }
- }
-
- if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
- EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
- System.identityHashCode(r), r.shortName,
- (r.app != null) ? r.app.pid : -1);
-
- mServiceMap.removeServiceByName(r.name, r.userId);
- mServiceMap.removeServiceByIntent(r.intent, r.userId);
- r.totalRestartCount = 0;
- unscheduleServiceRestartLocked(r);
-
- // Also make sure it is not on the pending list.
- int N = mPendingServices.size();
- for (int i=0; i<N; i++) {
- if (mPendingServices.get(i) == r) {
- mPendingServices.remove(i);
- if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
- i--;
- N--;
- }
- }
-
- r.cancelNotification();
- r.isForeground = false;
- r.foregroundId = 0;
- r.foregroundNoti = null;
-
- // Clear start entries.
- r.clearDeliveredStartsLocked();
- r.pendingStarts.clear();
-
- if (r.app != null) {
- synchronized (r.stats.getBatteryStats()) {
- r.stats.stopLaunchedLocked();
- }
- r.app.services.remove(r);
- if (r.app.thread != null) {
- try {
- bumpServiceExecutingLocked(r, "stop");
- mStoppingServices.add(r);
- updateOomAdjLocked(r.app);
- r.app.thread.scheduleStopService(r);
- } catch (Exception e) {
- Slog.w(TAG, "Exception when stopping service "
- + r.shortName, e);
- serviceDoneExecutingLocked(r, true);
- }
- updateServiceForegroundLocked(r.app, false);
- } else {
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed service that has no process: " + r);
- }
- } else {
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed service that is not running: " + r);
- }
-
- if (r.bindings.size() > 0) {
- r.bindings.clear();
- }
-
- if (r.restarter instanceof ServiceRestarter) {
- ((ServiceRestarter)r.restarter).setService(null);
- }
- }
-
- ComponentName startServiceLocked(IApplicationThread caller,
- Intent service, String resolvedType,
- int callingPid, int callingUid) {
- synchronized(this) {
- if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service
- + " type=" + resolvedType + " args=" + service.getExtras());
-
- if (caller != null) {
- final ProcessRecord callerApp = getRecordForAppLocked(caller);
- if (callerApp == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when starting service " + service);
- }
- }
-
- ServiceLookupResult res =
- retrieveServiceLocked(service, resolvedType,
- callingPid, callingUid, UserId.getUserId(callingUid));
- if (res == null) {
- return null;
- }
- if (res.record == null) {
- return new ComponentName("!", res.permission != null
- ? res.permission : "private to package");
- }
- ServiceRecord r = res.record;
- NeededUriGrants neededGrants = checkGrantUriPermissionFromIntentLocked(
- callingUid, r.packageName, service, service.getFlags(), null);
- if (unscheduleServiceRestartLocked(r)) {
- if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
- }
- r.startRequested = true;
- r.callStart = false;
- r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
- service, neededGrants));
- r.lastActivity = SystemClock.uptimeMillis();
- synchronized (r.stats.getBatteryStats()) {
- r.stats.startRunningLocked();
- }
- if (!bringUpServiceLocked(r, service.getFlags(), false)) {
- return new ComponentName("!", "Service process is bad");
- }
- return r.name;
- }
- }
-
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType) {
+ String resolvedType, int userId) {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
@@ -11783,9 +10566,10 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
+ checkValidCaller(callingUid, userId);
final long origId = Binder.clearCallingIdentity();
- ComponentName res = startServiceLocked(caller, service,
- resolvedType, callingPid, callingUid);
+ ComponentName res = mServices.startServiceLocked(caller, service,
+ resolvedType, callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -11797,60 +10581,26 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_SERVICE)
Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
- ComponentName res = startServiceLocked(null, service,
- resolvedType, -1, uid);
+ ComponentName res = mServices.startServiceLocked(null, service,
+ resolvedType, -1, uid, UserHandle.getUserId(uid));
Binder.restoreCallingIdentity(origId);
return res;
}
}
- private void stopServiceLocked(ServiceRecord service) {
- synchronized (service.stats.getBatteryStats()) {
- service.stats.stopRunningLocked();
- }
- service.startRequested = false;
- service.callStart = false;
- bringDownServiceLocked(service, false);
- }
-
public int stopService(IApplicationThread caller, Intent service,
- String resolvedType) {
+ String resolvedType, int userId) {
enforceNotIsolatedCaller("stopService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- synchronized(this) {
- if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service
- + " type=" + resolvedType);
-
- final ProcessRecord callerApp = getRecordForAppLocked(caller);
- if (caller != null && callerApp == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when stopping service " + service);
- }
+ checkValidCaller(Binder.getCallingUid(), userId);
- // If this service is active, make sure it is stopped.
- ServiceLookupResult r = findServiceLocked(service, resolvedType,
- callerApp == null ? UserId.getCallingUserId() : callerApp.userId);
- if (r != null) {
- if (r.record != null) {
- final long origId = Binder.clearCallingIdentity();
- try {
- stopServiceLocked(r.record);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- return 1;
- }
- return -1;
- }
+ synchronized(this) {
+ return mServices.stopServiceLocked(caller, service, resolvedType, userId);
}
-
- return 0;
}
public IBinder peekService(Intent service, String resolvedType) {
@@ -11859,149 +10609,51 @@ public final class ActivityManagerService extends ActivityManagerNative
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
-
- IBinder ret = null;
-
synchronized(this) {
- ServiceLookupResult r = findServiceLocked(service, resolvedType,
- UserId.getCallingUserId());
-
- if (r != null) {
- // r.record is null if findServiceLocked() failed the caller permission check
- if (r.record == null) {
- throw new SecurityException(
- "Permission Denial: Accessing service " + r.record.name
- + " from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + r.permission);
- }
- IntentBindRecord ib = r.record.bindings.get(r.record.intent);
- if (ib != null) {
- ret = ib.binder;
- }
- }
+ return mServices.peekServiceLocked(service, resolvedType);
}
-
- return ret;
}
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) {
synchronized(this) {
- if (DEBUG_SERVICE) Slog.v(TAG, "stopServiceToken: " + className
- + " " + token + " startId=" + startId);
- ServiceRecord r = findServiceLocked(className, token);
- if (r != null) {
- if (startId >= 0) {
- // Asked to only stop if done with all work. Note that
- // to avoid leaks, we will take this as dropping all
- // start items up to and including this one.
- ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
- if (si != null) {
- while (r.deliveredStarts.size() > 0) {
- ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
- cur.removeUriPermissionsLocked();
- if (cur == si) {
- break;
- }
- }
- }
-
- if (r.getLastStartId() != startId) {
- return false;
- }
-
- if (r.deliveredStarts.size() > 0) {
- Slog.w(TAG, "stopServiceToken startId " + startId
- + " is last, but have " + r.deliveredStarts.size()
- + " remaining args");
- }
- }
-
- synchronized (r.stats.getBatteryStats()) {
- r.stats.stopRunningLocked();
- r.startRequested = false;
- r.callStart = false;
- }
- final long origId = Binder.clearCallingIdentity();
- bringDownServiceLocked(r, false);
- Binder.restoreCallingIdentity(origId);
- return true;
- }
+ return mServices.stopServiceTokenLocked(className, token, startId);
}
- return false;
}
public void setServiceForeground(ComponentName className, IBinder token,
int id, Notification notification, boolean removeNotification) {
- final long origId = Binder.clearCallingIdentity();
- try {
synchronized(this) {
- ServiceRecord r = findServiceLocked(className, token);
- if (r != null) {
- if (id != 0) {
- if (notification == null) {
- throw new IllegalArgumentException("null notification");
- }
- if (r.foregroundId != id) {
- r.cancelNotification();
- r.foregroundId = id;
- }
- notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- r.foregroundNoti = notification;
- r.isForeground = true;
- r.postNotification();
- if (r.app != null) {
- updateServiceForegroundLocked(r.app, true);
- }
- } else {
- if (r.isForeground) {
- r.isForeground = false;
- if (r.app != null) {
- updateLruProcessLocked(r.app, false, true);
- updateServiceForegroundLocked(r.app, true);
- }
- }
- if (removeNotification) {
- r.cancelNotification();
- r.foregroundId = 0;
- r.foregroundNoti = null;
- }
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
- boolean anyForeground = false;
- for (ServiceRecord sr : proc.services) {
- if (sr.isForeground) {
- anyForeground = true;
- break;
- }
- }
- if (anyForeground != proc.foregroundServices) {
- proc.foregroundServices = anyForeground;
- if (oomAdj) {
- updateOomAdjLocked();
- }
+ mServices.setServiceForegroundLocked(className, token, id, notification,
+ removeNotification);
}
}
- boolean isSingleton(String componentProcessName, ApplicationInfo aInfo) {
+ boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
+ String className, int flags) {
boolean result = false;
- if (UserId.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
- result = false;
+ if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
+ if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ aInfo.uid) != PackageManager.PERMISSION_GRANTED) {
+ ComponentName comp = new ComponentName(aInfo.packageName, className);
+ String msg = "Permission Denial: Component " + comp.flattenToShortString()
+ + " requests FLAG_SINGLE_USER, but app does not hold "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ result = true;
+ }
} else if (componentProcessName == aInfo.packageName) {
result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
} else if ("system".equals(componentProcessName)) {
result = true;
}
if (DEBUG_MU) {
- Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo + ") = " + result);
+ Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo
+ + ", " + className + ", 0x" + Integer.toHexString(flags) + ") = " + result);
}
return result;
}
@@ -12018,236 +10670,15 @@ public final class ActivityManagerService extends ActivityManagerNative
checkValidCaller(Binder.getCallingUid(), userId);
synchronized(this) {
- if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
- + " type=" + resolvedType + " conn=" + connection.asBinder()
- + " flags=0x" + Integer.toHexString(flags));
- if (DEBUG_MU)
- Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
- + Binder.getOrigCallingUid());
- final ProcessRecord callerApp = getRecordForAppLocked(caller);
- if (callerApp == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when binding service " + service);
- }
-
- ActivityRecord activity = null;
- if (token != null) {
- activity = mMainStack.isInStackLocked(token);
- if (activity == null) {
- Slog.w(TAG, "Binding with unknown activity: " + token);
- return 0;
- }
- }
-
- int clientLabel = 0;
- PendingIntent clientIntent = null;
-
- if (callerApp.info.uid == Process.SYSTEM_UID) {
- // Hacky kind of thing -- allow system stuff to tell us
- // what they are, so we can report this elsewhere for
- // others to know why certain services are running.
- try {
- clientIntent = (PendingIntent)service.getParcelableExtra(
- Intent.EXTRA_CLIENT_INTENT);
- } catch (RuntimeException e) {
- }
- if (clientIntent != null) {
- clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
- if (clientLabel != 0) {
- // There are no useful extras in the intent, trash them.
- // System code calling with this stuff just needs to know
- // this will happen.
- service = service.cloneFilter();
- }
- }
- }
-
- ServiceLookupResult res =
- retrieveServiceLocked(service, resolvedType,
- Binder.getCallingPid(), Binder.getCallingUid(), userId);
- if (res == null) {
- return 0;
- }
- if (res.record == null) {
- return -1;
- }
- if (isSingleton(res.record.processName, res.record.appInfo)) {
- userId = 0;
- res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),
- Binder.getCallingUid(), 0);
- }
- ServiceRecord s = res.record;
-
- final long origId = Binder.clearCallingIdentity();
-
- if (unscheduleServiceRestartLocked(s)) {
- if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
- + s);
- }
-
- AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
- ConnectionRecord c = new ConnectionRecord(b, activity,
- connection, flags, clientLabel, clientIntent);
-
- IBinder binder = connection.asBinder();
- ArrayList<ConnectionRecord> clist = s.connections.get(binder);
- if (clist == null) {
- clist = new ArrayList<ConnectionRecord>();
- s.connections.put(binder, clist);
- }
- clist.add(c);
- b.connections.add(c);
- if (activity != null) {
- if (activity.connections == null) {
- activity.connections = new HashSet<ConnectionRecord>();
- }
- activity.connections.add(c);
- }
- b.client.connections.add(c);
- if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
- b.client.hasAboveClient = true;
- }
- clist = mServiceConnections.get(binder);
- if (clist == null) {
- clist = new ArrayList<ConnectionRecord>();
- mServiceConnections.put(binder, clist);
- }
- clist.add(c);
-
- if ((flags&Context.BIND_AUTO_CREATE) != 0) {
- s.lastActivity = SystemClock.uptimeMillis();
- if (!bringUpServiceLocked(s, service.getFlags(), false)) {
- return 0;
- }
- }
-
- if (s.app != null) {
- // This could have made the service more important.
- updateOomAdjLocked(s.app);
- }
-
- if (DEBUG_SERVICE) Slog.v(TAG, "Bind " + s + " with " + b
- + ": received=" + b.intent.received
- + " apps=" + b.intent.apps.size()
- + " doRebind=" + b.intent.doRebind);
-
- if (s.app != null && b.intent.received) {
- // Service is already running, so we can immediately
- // publish the connection.
- try {
- c.conn.connected(s.name, b.intent.binder);
- } catch (Exception e) {
- Slog.w(TAG, "Failure sending service " + s.shortName
- + " to connection " + c.conn.asBinder()
- + " (in " + c.binding.client.processName + ")", e);
- }
-
- // If this is the first app connected back to this binding,
- // and the service had previously asked to be told when
- // rebound, then do so.
- if (b.intent.apps.size() == 1 && b.intent.doRebind) {
- requestServiceBindingLocked(s, b.intent, true);
- }
- } else if (!b.intent.requested) {
- requestServiceBindingLocked(s, b.intent, false);
- }
-
- Binder.restoreCallingIdentity(origId);
- }
-
- return 1;
- }
-
- void removeConnectionLocked(
- ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
- IBinder binder = c.conn.asBinder();
- AppBindRecord b = c.binding;
- ServiceRecord s = b.service;
- ArrayList<ConnectionRecord> clist = s.connections.get(binder);
- if (clist != null) {
- clist.remove(c);
- if (clist.size() == 0) {
- s.connections.remove(binder);
- }
- }
- b.connections.remove(c);
- if (c.activity != null && c.activity != skipAct) {
- if (c.activity.connections != null) {
- c.activity.connections.remove(c);
- }
- }
- if (b.client != skipApp) {
- b.client.connections.remove(c);
- if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
- b.client.updateHasAboveClientLocked();
- }
- }
- clist = mServiceConnections.get(binder);
- if (clist != null) {
- clist.remove(c);
- if (clist.size() == 0) {
- mServiceConnections.remove(binder);
- }
- }
-
- if (b.connections.size() == 0) {
- b.intent.apps.remove(b.client);
- }
-
- if (!c.serviceDead) {
- if (DEBUG_SERVICE) Slog.v(TAG, "Disconnecting binding " + b.intent
- + ": shouldUnbind=" + b.intent.hasBound);
- if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
- && b.intent.hasBound) {
- try {
- bumpServiceExecutingLocked(s, "unbind");
- updateOomAdjLocked(s.app);
- b.intent.hasBound = false;
- // Assume the client doesn't want to know about a rebind;
- // we will deal with that later if it asks for one.
- b.intent.doRebind = false;
- s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
- } catch (Exception e) {
- Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
- serviceDoneExecutingLocked(s, true);
- }
- }
-
- if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
- bringDownServiceLocked(s, false);
- }
+ return mServices.bindServiceLocked(caller, token, service, resolvedType,
+ connection, flags, userId);
}
}
public boolean unbindService(IServiceConnection connection) {
synchronized (this) {
- IBinder binder = connection.asBinder();
- if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
- ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
- if (clist == null) {
- Slog.w(TAG, "Unbind failed: could not find connection for "
- + connection.asBinder());
- return false;
- }
-
- final long origId = Binder.clearCallingIdentity();
-
- while (clist.size() > 0) {
- ConnectionRecord r = clist.get(0);
- removeConnectionLocked(r, null, null);
-
- if (r.binding.service.app != null) {
- // This could have made the service less important.
- updateOomAdjLocked(r.binding.service.app);
- }
- }
-
- Binder.restoreCallingIdentity(origId);
+ return mServices.unbindServiceLocked(connection);
}
-
- return true;
}
public void publishService(IBinder token, Intent intent, IBinder service) {
@@ -12260,53 +10691,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
- ServiceRecord r = (ServiceRecord)token;
-
- final long origId = Binder.clearCallingIdentity();
-
- if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
- + " " + intent + ": " + service);
- if (r != null) {
- Intent.FilterComparison filter
- = new Intent.FilterComparison(intent);
- IntentBindRecord b = r.bindings.get(filter);
- if (b != null && !b.received) {
- b.binder = service;
- b.requested = true;
- b.received = true;
- if (r.connections.size() > 0) {
- Iterator<ArrayList<ConnectionRecord>> it
- = r.connections.values().iterator();
- while (it.hasNext()) {
- ArrayList<ConnectionRecord> clist = it.next();
- for (int i=0; i<clist.size(); i++) {
- ConnectionRecord c = clist.get(i);
- if (!filter.equals(c.binding.intent.intent)) {
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Not publishing to: " + c);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Bound intent: " + c.binding.intent.intent);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Published intent: " + intent);
- continue;
- }
- if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
- try {
- c.conn.connected(r.name, service);
- } catch (Exception e) {
- Slog.w(TAG, "Failure sending service " + r.name +
- " to connection " + c.conn.asBinder() +
- " (in " + c.binding.client.processName + ")", e);
- }
- }
- }
- }
- }
-
- serviceDoneExecutingLocked(r, mStoppingServices.contains(r));
-
- Binder.restoreCallingIdentity(origId);
- }
+ mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
@@ -12317,38 +10702,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
synchronized(this) {
- if (!(token instanceof ServiceRecord)) {
- throw new IllegalArgumentException("Invalid service token");
- }
- ServiceRecord r = (ServiceRecord)token;
-
- final long origId = Binder.clearCallingIdentity();
-
- if (r != null) {
- Intent.FilterComparison filter
- = new Intent.FilterComparison(intent);
- IntentBindRecord b = r.bindings.get(filter);
- if (DEBUG_SERVICE) Slog.v(TAG, "unbindFinished in " + r
- + " at " + b + ": apps="
- + (b != null ? b.apps.size() : 0));
-
- boolean inStopping = mStoppingServices.contains(r);
- if (b != null) {
- if (b.apps.size() > 0 && !inStopping) {
- // Applications have already bound since the last
- // unbind, so just rebind right here.
- requestServiceBindingLocked(r, b, true);
- } else {
- // Note to tell the service the next time there is
- // a new client.
- b.doRebind = true;
- }
- }
-
- serviceDoneExecutingLocked(r, inStopping);
-
- Binder.restoreCallingIdentity(origId);
- }
+ mServices.unbindFinishedLocked((ServiceRecord)token, intent, doRebind);
}
}
@@ -12357,137 +10711,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
- ServiceRecord r = (ServiceRecord)token;
- boolean inStopping = mStoppingServices.contains(token);
- if (r != null) {
- if (r != token) {
- Slog.w(TAG, "Done executing service " + r.name
- + " with incorrect token: given " + token
- + ", expected " + r);
- return;
- }
-
- if (type == 1) {
- // This is a call from a service start... take care of
- // book-keeping.
- r.callStart = true;
- switch (res) {
- case Service.START_STICKY_COMPATIBILITY:
- case Service.START_STICKY: {
- // We are done with the associated start arguments.
- r.findDeliveredStart(startId, true);
- // Don't stop if killed.
- r.stopIfKilled = false;
- break;
- }
- case Service.START_NOT_STICKY: {
- // We are done with the associated start arguments.
- r.findDeliveredStart(startId, true);
- if (r.getLastStartId() == startId) {
- // There is no more work, and this service
- // doesn't want to hang around if killed.
- r.stopIfKilled = true;
- }
- break;
- }
- case Service.START_REDELIVER_INTENT: {
- // We'll keep this item until they explicitly
- // call stop for it, but keep track of the fact
- // that it was delivered.
- ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
- if (si != null) {
- si.deliveryCount = 0;
- si.doneExecutingCount++;
- // Don't stop if killed.
- r.stopIfKilled = true;
- }
- break;
- }
- case Service.START_TASK_REMOVED_COMPLETE: {
- // Special processing for onTaskRemoved(). Don't
- // impact normal onStartCommand() processing.
- r.findDeliveredStart(startId, true);
- break;
- }
- default:
- throw new IllegalArgumentException(
- "Unknown service start result: " + res);
- }
- if (res == Service.START_STICKY_COMPATIBILITY) {
- r.callStart = false;
- }
- }
- if (DEBUG_MU)
- Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
- + Binder.getOrigCallingUid());
- final long origId = Binder.clearCallingIdentity();
- serviceDoneExecutingLocked(r, inStopping);
- Binder.restoreCallingIdentity(origId);
- } else {
- Slog.w(TAG, "Done executing unknown service from pid "
- + Binder.getCallingPid());
- }
- }
- }
-
- public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
- if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
- + ": nesting=" + r.executeNesting
- + ", inStopping=" + inStopping + ", app=" + r.app);
- else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
- r.executeNesting--;
- if (r.executeNesting <= 0 && r.app != null) {
- if (DEBUG_SERVICE) Slog.v(TAG,
- "Nesting at 0 of " + r.shortName);
- r.app.executingServices.remove(r);
- if (r.app.executingServices.size() == 0) {
- if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
- "No more executingServices of " + r.shortName);
- mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
- }
- if (inStopping) {
- if (DEBUG_SERVICE) Slog.v(TAG,
- "doneExecuting remove stopping " + r);
- mStoppingServices.remove(r);
- r.bindings.clear();
- }
- updateOomAdjLocked(r.app);
- }
- }
-
- void serviceTimeout(ProcessRecord proc) {
- String anrMessage = null;
-
- synchronized(this) {
- if (proc.executingServices.size() == 0 || proc.thread == null) {
- return;
- }
- long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT;
- Iterator<ServiceRecord> it = proc.executingServices.iterator();
- ServiceRecord timeout = null;
- long nextTime = 0;
- while (it.hasNext()) {
- ServiceRecord sr = it.next();
- if (sr.executingStart < maxTime) {
- timeout = sr;
- break;
- }
- if (sr.executingStart > nextTime) {
- nextTime = sr.executingStart;
- }
- }
- if (timeout != null && mLruProcesses.contains(proc)) {
- Slog.w(TAG, "Timeout executing service: " + timeout);
- anrMessage = "Executing service " + timeout.shortName;
- } else {
- Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
- msg.obj = proc;
- mHandler.sendMessageAtTime(msg, nextTime+SERVICE_TIMEOUT);
- }
- }
-
- if (anrMessage != null) {
- appNotResponding(proc, null, null, anrMessage);
+ mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
}
}
@@ -12513,7 +10737,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Backup agent is now in use, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- app.packageName, false, UserId.getUserId(app.uid));
+ app.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
@@ -12674,6 +10898,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission) {
enforceNotIsolatedCaller("registerReceiver");
+ int callingUid;
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) {
@@ -12689,8 +10914,10 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
+ callingUid = callerApp.info.uid;
} else {
callerPackage = null;
+ callingUid = Binder.getCallingUid();
}
List allSticky = null;
@@ -12735,7 +10962,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
- BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission);
+ BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
+ permission, callingUid);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
@@ -12754,7 +10982,7 @@ public final class ActivityManagerService extends ActivityManagerNative
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, receivers, null, 0, null, null,
- false, true, true);
+ false, true, true, false);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
@@ -12846,7 +11074,34 @@ public final class ActivityManagerService extends ActivityManagerNative
if ((resultTo != null) && !ordered) {
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
-
+
+ boolean onlySendToCaller = false;
+
+ // If the caller is trying to send this broadcast to a different
+ // user, verify that is allowed.
+ if (UserHandle.getUserId(callingUid) != userId) {
+ if (checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ callingPid, callingUid, -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ callingPid, callingUid, -1, true)
+ == PackageManager.PERMISSION_GRANTED) {
+ onlySendToCaller = true;
+ } else {
+ String msg = "Permission Denial: " + intent.getAction()
+ + " broadcast from " + callerPackage
+ + " asks to send as user " + userId
+ + " but is calling from user " + UserHandle.getUserId(callingUid)
+ + "; this requires "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+ }
+
// Handle special intents: if this broadcast is from the package
// manager about a package being removed, we need to remove all of
// its activities from the history stack.
@@ -12942,7 +11197,8 @@ public final class ActivityManagerService extends ActivityManagerNative
* processes) from sending protected broadcasts.
*/
if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
- || callingUid == Process.SHELL_UID || callingUid == 0) {
+ || callingUid == Process.SHELL_UID || callingUid == Process.BLUETOOTH_UID ||
+ callingUid == 0) {
// Always okay.
} else if (callerApp == null || !callerApp.persistent) {
try {
@@ -13003,30 +11259,15 @@ public final class ActivityManagerService extends ActivityManagerNative
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
try {
- if (intent.getComponent() != null) {
- // Broadcast is going to one specific receiver class...
- ActivityInfo ai = AppGlobals.getPackageManager().
- getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS, userId);
- if (ai != null) {
- receivers = new ArrayList();
- ResolveInfo ri = new ResolveInfo();
- if (isSingleton(ai.processName, ai.applicationInfo)) {
- ri.activityInfo = getActivityInfoForUser(ai, 0);
- } else {
- ri.activityInfo = getActivityInfoForUser(ai, userId);
- }
- receivers.add(ri);
- }
- } else {
- // Need to resolve the intent to interested receivers...
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
- == 0) {
- receivers =
- AppGlobals.getPackageManager().queryIntentReceivers(
- intent, resolvedType, STOCK_PM_FLAGS, userId);
- }
- registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false,
- userId);
+ // Need to resolve the intent to interested receivers...
+ if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ == 0) {
+ receivers = AppGlobals.getPackageManager().queryIntentReceivers(
+ intent, resolvedType, STOCK_PM_FLAGS, userId);
+ }
+ if (intent.getComponent() == null) {
+ registeredReceivers = mReceiverResolver.queryIntent(intent,
+ resolvedType, false, userId);
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
@@ -13047,7 +11288,7 @@ public final class ActivityManagerService extends ActivityManagerNative
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
registeredReceivers, resultTo, resultCode, resultData, map,
- ordered, sticky, false);
+ ordered, sticky, false, onlySendToCaller);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
@@ -13137,7 +11378,7 @@ public final class ActivityManagerService extends ActivityManagerNative
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
receivers, resultTo, resultCode, resultData, map, ordered,
- sticky, false);
+ sticky, false, onlySendToCaller);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
@@ -13345,7 +11586,7 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId);
@@ -13408,7 +11649,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public void finishInstrumentation(IApplicationThread target,
int resultCode, Bundle results) {
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
// Refuse possible leaked file descriptors
if (results != null && results.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -13721,7 +11962,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} else {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
- destIntent.getComponent(), 0, UserId.getCallingUserId());
+ destIntent.getComponent(), 0, UserHandle.getCallingUserId());
int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
null, aInfo, parent.appToken, null,
0, -1, parent.launchedFromUid, 0, null, true, null);
@@ -13773,14 +12014,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
- ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
+ int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
if (mAdjSeq == app.adjSeq) {
// This adjustment has already been computed. If we are calling
// from the top, we may have already computed our adjustment with
// an earlier hidden adjustment that isn't really for us... if
// so, use the new hidden adjustment.
if (!recursed && app.hidden) {
- app.curAdj = app.curRawAdj = app.nonStoppingAdj = hiddenAdj;
+ app.curAdj = app.curRawAdj = app.nonStoppingAdj =
+ app.hasActivities ? hiddenAdj : emptyAdj;
}
return app.curRawAdj;
}
@@ -13788,7 +12030,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.thread == null) {
app.adjSeq = mAdjSeq;
app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- return (app.curAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
+ return (app.curAdj=app.curRawAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
}
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
@@ -13805,6 +12047,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.adjType = "fixed";
app.adjSeq = mAdjSeq;
app.curRawAdj = app.nonStoppingAdj = app.maxAdj;
+ app.hasActivities = false;
app.foregroundActivities = false;
app.keeping = true;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -13815,12 +12058,15 @@ public final class ActivityManagerService extends ActivityManagerNative
app.systemNoUi = true;
if (app == TOP_APP) {
app.systemNoUi = false;
+ app.hasActivities = true;
} else if (activitiesSize > 0) {
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = app.activities.get(j);
if (r.visible) {
app.systemNoUi = false;
- break;
+ }
+ if (r.app == app) {
+ app.hasActivities = true;
}
}
}
@@ -13829,6 +12075,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.keeping = false;
app.systemNoUi = false;
+ app.hasActivities = false;
// Determine the importance of the process, starting with most
// important to least, and assign an appropriate OOM adjustment.
@@ -13844,6 +12091,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.adjType = "top-activity";
foregroundActivities = true;
interesting = true;
+ app.hasActivities = true;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -13865,21 +12113,13 @@ public final class ActivityManagerService extends ActivityManagerNative
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "exec-service";
- } else if (activitiesSize > 0) {
- // This app is in the background with paused activities.
- // We inspect activities to potentially upgrade adjustment further below.
- adj = hiddenAdj;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.hidden = true;
- app.adjType = "bg-activities";
} else {
- // A very not-needed process. If this is lower in the lru list,
- // we will push it in to the empty bucket.
+ // Assume process is hidden (has activities); we will correct
+ // later if this is not the case.
adj = hiddenAdj;
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.hidden = true;
- app.empty = true;
- app.adjType = "bg-empty";
+ app.adjType = "bg-activities";
}
boolean hasStoppingActivities = false;
@@ -13896,6 +12136,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.hidden = false;
+ app.hasActivities = true;
foregroundActivities = true;
break;
} else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
@@ -13913,9 +12154,20 @@ public final class ActivityManagerService extends ActivityManagerNative
foregroundActivities = true;
hasStoppingActivities = true;
}
+ if (r.app == app) {
+ app.hasActivities = true;
+ }
}
}
+ if (adj == hiddenAdj && !app.hasActivities) {
+ // Whoops, this process is completely empty as far as we know
+ // at this point.
+ adj = emptyAdj;
+ app.empty = true;
+ app.adjType = "bg-empty";
+ }
+
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
@@ -14003,7 +12255,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.adjType = "started-bg-ui-services";
}
} else {
- if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
+ if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
// recent memory, so we will keep its process ahead
// of the background processes.
@@ -14049,8 +12301,16 @@ public final class ActivityManagerService extends ActivityManagerNative
myHiddenAdj = ProcessList.VISIBLE_APP_ADJ;
}
}
- clientAdj = computeOomAdjLocked(
- client, myHiddenAdj, TOP_APP, true, doingAll);
+ int myEmptyAdj = emptyAdj;
+ if (myEmptyAdj > client.emptyAdj) {
+ if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) {
+ myEmptyAdj = client.emptyAdj;
+ } else {
+ myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
+ }
+ }
+ clientAdj = computeOomAdjLocked(client, myHiddenAdj,
+ myEmptyAdj, TOP_APP, true, doingAll);
String adjType = null;
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
// Not doing bind OOM management, so treat
@@ -14066,7 +12326,8 @@ public final class ActivityManagerService extends ActivityManagerNative
app.hidden = false;
clientAdj = adj;
} else {
- if (now >= (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
+ if (now >= (s.lastActivity
+ + ActiveServices.MAX_SERVICE_INACTIVITY)) {
// This service has not seen activity within
// recent memory, so allow it to drop to the
// LRU list if there is no other reason to keep
@@ -14188,8 +12449,16 @@ public final class ActivityManagerService extends ActivityManagerNative
myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
}
}
- int clientAdj = computeOomAdjLocked(
- client, myHiddenAdj, TOP_APP, true, doingAll);
+ int myEmptyAdj = emptyAdj;
+ if (myEmptyAdj > client.emptyAdj) {
+ if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) {
+ myEmptyAdj = client.emptyAdj;
+ } else {
+ myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
+ }
+ }
+ int clientAdj = computeOomAdjLocked(client, myHiddenAdj,
+ myEmptyAdj, TOP_APP, true, doingAll);
if (adj > clientAdj) {
if (app.hasShownUi && app != mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -14602,9 +12871,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private final boolean updateOomAdjLocked(
- ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP, boolean doingAll) {
+ private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj,
+ int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) {
app.hiddenAdj = hiddenAdj;
+ app.emptyAdj = emptyAdj;
if (app.thread == null) {
return false;
@@ -14614,7 +12884,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean success = true;
- computeOomAdjLocked(app, hiddenAdj, TOP_APP, false, doingAll);
+ computeOomAdjLocked(app, hiddenAdj, emptyAdj, TOP_APP, false, doingAll);
if (app.curRawAdj != app.setRawAdj) {
if (wasKeeping && !app.keeping) {
@@ -14692,7 +12962,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return resumedActivity;
}
- private final boolean updateOomAdjLocked(ProcessRecord app) {
+ final boolean updateOomAdjLocked(ProcessRecord app) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
int curAdj = app.curAdj;
@@ -14701,7 +12971,8 @@ public final class ActivityManagerService extends ActivityManagerNative
mAdjSeq++;
- boolean success = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP, false);
+ boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.emptyAdj,
+ TOP_APP, false);
final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
&& app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
if (nowHidden != wasHidden) {
@@ -14729,34 +13000,53 @@ public final class ActivityManagerService extends ActivityManagerNative
// how many slots we have for background processes; we may want
// to put multiple processes in a slot of there are enough of
// them.
- int numSlots = ProcessList.HIDDEN_APP_MAX_ADJ - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
- int factor = (mLruProcesses.size()-4)/numSlots;
- if (factor < 1) factor = 1;
- int step = 0;
+ int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ
+ - ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2;
+ int emptyFactor = (mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs)/numSlots;
+ if (emptyFactor < 1) emptyFactor = 1;
+ int hiddenFactor = (mNumHiddenProcs > 0 ? mNumHiddenProcs : 1)/numSlots;
+ if (hiddenFactor < 1) hiddenFactor = 1;
+ int stepHidden = 0;
+ int stepEmpty = 0;
+ final int emptyProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit;
+ final int hiddenProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit;
int numHidden = 0;
+ int numEmpty = 0;
int numTrimming = 0;
-
+
+ mNumNonHiddenProcs = 0;
+ mNumHiddenProcs = 0;
+
// First update the OOM adjustment for each of the
// application processes based on their current state.
int i = mLruProcesses.size();
int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+ int nextHiddenAdj = curHiddenAdj+1;
+ int curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+ int nextEmptyAdj = curEmptyAdj+2;
while (i > 0) {
i--;
ProcessRecord app = mLruProcesses.get(i);
//Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj);
- updateOomAdjLocked(app, curHiddenAdj, TOP_APP, true);
- if (curHiddenAdj < ProcessList.HIDDEN_APP_MAX_ADJ
- && app.curAdj == curHiddenAdj) {
- step++;
- if (step >= factor) {
- step = 0;
- curHiddenAdj++;
- }
- }
+ updateOomAdjLocked(app, curHiddenAdj, curEmptyAdj, TOP_APP, true);
if (!app.killedBackground) {
- if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ if (app.curRawAdj == curHiddenAdj && app.hasActivities) {
+ // This process was assigned as a hidden process... step the
+ // hidden level.
+ mNumHiddenProcs++;
+ if (curHiddenAdj != nextHiddenAdj) {
+ stepHidden++;
+ if (stepHidden >= hiddenFactor) {
+ stepHidden = 0;
+ curHiddenAdj = nextHiddenAdj;
+ nextHiddenAdj += 2;
+ if (nextHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
+ nextHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+ }
+ }
+ }
numHidden++;
- if (numHidden > mProcessLimit) {
+ if (numHidden > hiddenProcessLimit) {
Slog.i(TAG, "No longer want " + app.processName
+ " (pid " + app.pid + "): hidden #" + numHidden);
EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
@@ -14764,8 +13054,37 @@ public final class ActivityManagerService extends ActivityManagerNative
app.killedBackground = true;
Process.killProcessQuiet(app.pid);
}
+ } else {
+ if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curHiddenAdj) {
+ // This process was assigned as an empty process... step the
+ // empty level.
+ if (curEmptyAdj != nextEmptyAdj) {
+ stepEmpty++;
+ if (stepEmpty >= emptyFactor) {
+ stepEmpty = 0;
+ curEmptyAdj = nextEmptyAdj;
+ nextEmptyAdj += 2;
+ if (nextEmptyAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
+ nextEmptyAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+ }
+ }
+ }
+ } else if (app.curRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) {
+ mNumNonHiddenProcs++;
+ }
+ if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ numEmpty++;
+ if (numEmpty > emptyProcessLimit) {
+ Slog.i(TAG, "No longer want " + app.processName
+ + " (pid " + app.pid + "): empty #" + numEmpty);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "too many background");
+ app.killedBackground = true;
+ Process.killProcessQuiet(app.pid);
+ }
+ }
}
- if (!app.killedBackground && app.isolated && app.services.size() <= 0) {
+ if (app.isolated && app.services.size() <= 0) {
// If this is an isolated process, and there are no
// services running in it, then the process is no longer
// needed. We agressively kill these because we can by
@@ -14795,18 +13114,20 @@ public final class ActivityManagerService extends ActivityManagerNative
// are managing to keep around is less than half the maximum we desire;
// if we are keeping a good number around, we'll let them use whatever
// memory they want.
- if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/2)) {
+ if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/4)
+ && numEmpty <= (ProcessList.MAX_HIDDEN_APPS/4)) {
+ final int numHiddenAndEmpty = numHidden + numEmpty;
final int N = mLruProcesses.size();
- factor = numTrimming/3;
+ int factor = numTrimming/3;
int minFactor = 2;
if (mHomeProcess != null) minFactor++;
if (mPreviousProcess != null) minFactor++;
if (factor < minFactor) factor = minFactor;
- step = 0;
+ int step = 0;
int fgTrimLevel;
- if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/5)) {
+ if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/5)) {
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
- } else if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/3)) {
+ } else if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/3)) {
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
} else {
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
@@ -15165,9 +13486,6 @@ public final class ActivityManagerService extends ActivityManagerNative
// Multi-user methods
- private int mCurrentUserId;
- private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
-
public boolean switchUser(int userId) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.myUid()) {
@@ -15197,8 +13515,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// Inform of user switch
Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userId);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
return true;
}
@@ -15210,11 +13528,11 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.e(TAG, "Trying to get user from unauthorized app");
return null;
}
- return AppGlobals.getPackageManager().getUser(mCurrentUserId);
+ return getUserManager().getUserInfo(mCurrentUserId);
}
private void onUserRemoved(Intent intent) {
- int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (extraUserId < 1) return;
// Kill all the processes for the user
@@ -15224,7 +13542,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
SparseArray<ProcessRecord> uids = uidMap.getValue();
for (int i = 0; i < uids.size(); i++) {
- if (UserId.getUserId(uids.keyAt(i)) == extraUserId) {
+ if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) {
pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
}
}
@@ -15238,28 +13556,29 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private boolean userExists(int userId) {
- try {
- UserInfo user = AppGlobals.getPackageManager().getUser(userId);
- return user != null;
- } catch (RemoteException re) {
- // Won't happen, in same process
- }
+ UserInfo user = getUserManager().getUserInfo(userId);
+ return user != null;
+ }
- return false;
+ UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+ return mUserManager;
}
private void checkValidCaller(int uid, int userId) {
- if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
+ if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
throw new SecurityException("Caller uid=" + uid
+ " is not privileged to communicate with user=" + userId);
}
private int applyUserId(int uid, int userId) {
- return UserId.getUid(userId, uid);
+ return UserHandle.getUid(userId, uid);
}
- private ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
+ ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
if (info == null) return null;
ApplicationInfo newInfo = new ApplicationInfo(info);
newInfo.uid = applyUserId(info.uid, userId);
@@ -15270,7 +13589,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
if (aInfo == null
- || (userId < 1 && aInfo.applicationInfo.uid < UserId.PER_USER_RANGE)) {
+ || (userId < 1 && aInfo.applicationInfo.uid < UserHandle.PER_USER_RANGE)) {
return aInfo;
}
@@ -15278,86 +13597,4 @@ public final class ActivityManagerService extends ActivityManagerNative
info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId);
return info;
}
-
- static class ServiceMap {
-
- private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser
- = new SparseArray<HashMap<ComponentName, ServiceRecord>>();
- private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>>
- mServicesByIntentPerUser = new SparseArray<
- HashMap<Intent.FilterComparison, ServiceRecord>>();
-
- ServiceRecord getServiceByName(ComponentName name, int callingUser) {
- // TODO: Deal with global services
- if (DEBUG_MU)
- Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser);
- return getServices(callingUser).get(name);
- }
-
- ServiceRecord getServiceByName(ComponentName name) {
- return getServiceByName(name, -1);
- }
-
- ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) {
- // TODO: Deal with global services
- if (DEBUG_MU)
- Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser);
- return getServicesByIntent(callingUser).get(filter);
- }
-
- ServiceRecord getServiceByIntent(Intent.FilterComparison filter) {
- return getServiceByIntent(filter, -1);
- }
-
- void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) {
- // TODO: Deal with global services
- getServices(callingUser).put(name, value);
- }
-
- void putServiceByIntent(Intent.FilterComparison filter, int callingUser,
- ServiceRecord value) {
- // TODO: Deal with global services
- getServicesByIntent(callingUser).put(filter, value);
- }
-
- void removeServiceByName(ComponentName name, int callingUser) {
- // TODO: Deal with global services
- ServiceRecord removed = getServices(callingUser).remove(name);
- if (DEBUG_MU)
- Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name
- + " removed=" + removed);
- }
-
- void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) {
- // TODO: Deal with global services
- ServiceRecord removed = getServicesByIntent(callingUser).remove(filter);
- if (DEBUG_MU)
- Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter
- + " removed=" + removed);
- }
-
- Collection<ServiceRecord> getAllServices(int callingUser) {
- // TODO: Deal with global services
- return getServices(callingUser).values();
- }
-
- private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) {
- HashMap map = mServicesByNamePerUser.get(callingUser);
- if (map == null) {
- map = new HashMap<ComponentName, ServiceRecord>();
- mServicesByNamePerUser.put(callingUser, map);
- }
- return map;
- }
-
- private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent(
- int callingUser) {
- HashMap map = mServicesByIntentPerUser.get(callingUser);
- if (map == null) {
- map = new HashMap<Intent.FilterComparison, ServiceRecord>();
- mServicesByIntentPerUser.put(callingUser, map);
- }
- return map;
- }
- }
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c40abb7..c70650d 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -37,7 +37,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -321,7 +321,7 @@ final class ActivityRecord {
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
- userId = UserId.getUserId(aInfo.applicationInfo.uid);
+ userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
@@ -614,14 +614,14 @@ final class ActivityRecord {
pendingOptions.getStartY()+pendingOptions.getStartHeight()));
}
break;
- case ActivityOptions.ANIM_THUMBNAIL:
- case ActivityOptions.ANIM_THUMBNAIL_DELAYED:
- boolean delayed = (animationType == ActivityOptions.ANIM_THUMBNAIL_DELAYED);
+ case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP:
+ case ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN:
+ boolean scaleUp = (animationType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP);
service.mWindowManager.overridePendingAppTransitionThumb(
pendingOptions.getThumbnail(),
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getOnAnimationStartListener(),
- delayed);
+ scaleUp);
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 9171e47..ccea41a 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -55,10 +55,11 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.view.Display;
import android.view.WindowManagerPolicy;
import java.io.IOException;
@@ -500,7 +501,7 @@ final class ActivityStack {
TaskRecord cp = null;
- final int userId = UserId.getUserId(info.applicationInfo.uid);
+ final int userId = UserHandle.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
@@ -544,7 +545,7 @@ final class ActivityStack {
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
- final int userId = UserId.getUserId(info.applicationInfo.uid);
+ final int userId = UserHandle.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
@@ -902,7 +903,7 @@ final class ActivityStack {
mService.notifyAll();
}
}
-
+
public final Bitmap screenshotActivities(ActivityRecord who) {
if (who.noDisplay) {
return null;
@@ -919,7 +920,8 @@ final class ActivityStack {
}
if (w > 0) {
- return mService.mWindowManager.screenshotApplications(who.appToken, w, h);
+ return mService.mWindowManager.screenshotApplications(who.appToken,
+ Display.DEFAULT_DISPLAY, w, h);
}
return null;
}
@@ -2404,7 +2406,7 @@ final class ActivityStack {
}
if (err == ActivityManager.START_SUCCESS) {
- final int userId = aInfo != null ? UserId.getUserId(aInfo.applicationInfo.uid) : 0;
+ final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
Slog.i(TAG, "START {" + intent.toShortString(true, true, true, false)
+ " u=" + userId + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
}
@@ -3012,7 +3014,8 @@ final class ActivityStack {
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
profileFile, profileFd, userId);
- if (aInfo != null && mService.isSingleton(aInfo.processName, aInfo.applicationInfo)) {
+ if (aInfo != null && (aInfo.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
+ && mService.isSingleton(aInfo.processName, aInfo.applicationInfo, null, 0)) {
userId = 0;
}
aInfo = mService.getActivityInfoForUser(aInfo, userId);
@@ -3900,7 +3903,7 @@ final class ActivityStack {
Iterator<ConnectionRecord> it = r.connections.iterator();
while (it.hasNext()) {
ConnectionRecord c = it.next();
- mService.removeConnectionLocked(c, null, r);
+ mService.mServices.removeConnectionLocked(c, null, r);
}
r.connections = null;
}
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 8f797ec..ab20208 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -346,18 +346,18 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
mStats.noteFullWifiLockReleasedLocked(uid);
}
}
-
- public void noteScanWifiLockAcquired(int uid) {
+
+ public void noteWifiScanStarted(int uid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteScanWifiLockAcquiredLocked(uid);
+ mStats.noteWifiScanStartedLocked(uid);
}
}
-
- public void noteScanWifiLockReleased(int uid) {
+
+ public void noteWifiScanStopped(int uid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteScanWifiLockReleasedLocked(uid);
+ mStats.noteWifiScanStoppedLocked(uid);
}
}
@@ -389,17 +389,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
- public void noteScanWifiLockAcquiredFromSource(WorkSource ws) {
+ public void noteWifiScanStartedFromSource(WorkSource ws) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteScanWifiLockAcquiredFromSourceLocked(ws);
+ mStats.noteWifiScanStartedFromSourceLocked(ws);
}
}
- public void noteScanWifiLockReleasedFromSource(WorkSource ws) {
+ public void noteWifiScanStoppedFromSource(WorkSource ws) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteScanWifiLockReleasedFromSourceLocked(ws);
+ mStats.noteWifiScanStoppedFromSourceLocked(ws);
}
}
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index b49bc22..4e6d0fa 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -27,13 +27,15 @@ class BroadcastFilter extends IntentFilter {
final ReceiverList receiverList;
final String packageName;
final String requiredPermission;
+ final int owningUid;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _packageName, String _requiredPermission) {
+ String _packageName, String _requiredPermission, int _owningUid) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
requiredPermission = _requiredPermission;
+ owningUid = _owningUid;
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 47b8c0a..7873dd8 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -20,12 +20,15 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -33,7 +36,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
@@ -249,7 +252,7 @@ public class BroadcastQueue {
finishReceiverLocked(br, br.resultCode, br.resultData,
br.resultExtras, br.resultAbort, true);
scheduleBroadcastsLocked();
- // We need to reset the state if we fails to start the receiver.
+ // We need to reset the state if we failed to start the receiver.
br.state = BroadcastRecord.IDLE;
throw new RuntimeException(e.getMessage());
}
@@ -369,7 +372,17 @@ public class BroadcastQueue {
private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered) {
boolean skip = false;
- if (filter.requiredPermission != null) {
+ if (r.onlySendToCaller) {
+ if (!UserHandle.isSameApp(r.callingUid, filter.owningUid)) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid="
+ + r.callingPid + ", uid=" + r.callingUid + ")"
+ + " not allowed to go to different app " + filter.owningUid);
+ skip = true;
+ }
+ }
+ if (!skip && filter.requiredPermission != null) {
int perm = mService.checkComponentPermission(filter.requiredPermission,
r.callingPid, r.callingUid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
@@ -382,7 +395,7 @@ public class BroadcastQueue {
skip = true;
}
}
- if (r.requiredPermission != null) {
+ if (!skip && r.requiredPermission != null) {
int perm = mService.checkComponentPermission(r.requiredPermission,
filter.receiverList.pid, filter.receiverList.uid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
@@ -649,8 +662,23 @@ public class BroadcastQueue {
ResolveInfo info =
(ResolveInfo)nextReceiver;
+ ComponentName component = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
boolean skip = false;
+ if (r.onlySendToCaller) {
+ if (!UserHandle.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid="
+ + r.callingPid + ", uid=" + r.callingUid + ")"
+ + " to " + component.flattenToShortString()
+ + " not allowed to go to different app "
+ + info.activityInfo.applicationInfo.uid);
+ skip = true;
+ }
+ }
int perm = mService.checkComponentPermission(info.activityInfo.permission,
r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
info.activityInfo.exported);
@@ -661,16 +689,14 @@ public class BroadcastQueue {
+ " from " + r.callerPackage + " (pid=" + r.callingPid
+ ", uid=" + r.callingUid + ")"
+ " is not exported from uid " + info.activityInfo.applicationInfo.uid
- + " due to receiver " + info.activityInfo.packageName
- + "/" + info.activityInfo.name);
+ + " due to receiver " + component.flattenToShortString());
} else {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid=" + r.callingPid
+ ", uid=" + r.callingUid + ")"
+ " requires " + info.activityInfo.permission
- + " due to receiver " + info.activityInfo.packageName
- + "/" + info.activityInfo.name);
+ + " due to receiver " + component.flattenToShortString());
}
skip = true;
}
@@ -686,13 +712,33 @@ public class BroadcastQueue {
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: receiving "
+ r.intent + " to "
- + info.activityInfo.applicationInfo.packageName
+ + component.flattenToShortString()
+ " requires " + r.requiredPermission
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")");
skip = true;
}
}
+ boolean isSingleton = false;
+ try {
+ isSingleton = mService.isSingleton(info.activityInfo.processName,
+ info.activityInfo.applicationInfo,
+ info.activityInfo.name, info.activityInfo.flags);
+ } catch (SecurityException e) {
+ Slog.w(TAG, e.getMessage());
+ skip = true;
+ }
+ if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ info.activityInfo.applicationInfo.uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
+ + " requests FLAG_SINGLE_USER, but app does not hold "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS);
+ skip = true;
+ }
+ }
if (r.curApp != null && r.curApp.crashing) {
// If the target process is crashing, just skip it.
if (DEBUG_BROADCAST) Slog.v(TAG,
@@ -715,17 +761,12 @@ public class BroadcastQueue {
r.state = BroadcastRecord.APP_RECEIVE;
String targetProcess = info.activityInfo.processName;
- r.curComponent = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
- if (r.callingUid != Process.SYSTEM_UID) {
- boolean isSingleton = mService.isSingleton(info.activityInfo.processName,
- info.activityInfo.applicationInfo);
- int targetUserId = isSingleton ? 0 : UserId.getUserId(r.callingUid);
- info.activityInfo = mService.getActivityInfoForUser(info.activityInfo,targetUserId);
+ r.curComponent = component;
+ if (r.callingUid != Process.SYSTEM_UID && isSingleton) {
+ info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
}
r.curReceiver = info.activityInfo;
- if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+ if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ info.activityInfo.applicationInfo.uid);
@@ -734,7 +775,7 @@ public class BroadcastQueue {
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, UserId.getUserId(r.callingUid));
+ r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index dd560fc..799b609 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -44,6 +44,7 @@ class BroadcastRecord extends Binder {
final boolean ordered; // serialize the send to receivers?
final boolean sticky; // originated from existing sticky data?
final boolean initialSticky; // initial broadcast from register to sticky?
+ final boolean onlySendToCaller; // only allow receipt by sender's components?
final String requiredPermission; // a permission the caller has required
final List receivers; // contains BroadcastFilter and ResolveInfo
IIntentReceiver resultTo; // who receives final result if non-null
@@ -167,7 +168,7 @@ class BroadcastRecord extends Binder {
int _callingPid, int _callingUid, String _requiredPermission,
List _receivers, IIntentReceiver _resultTo, int _resultCode,
String _resultData, Bundle _resultExtras, boolean _serialized,
- boolean _sticky, boolean _initialSticky) {
+ boolean _sticky, boolean _initialSticky, boolean _onlySendToCaller) {
queue = _queue;
intent = _intent;
callerApp = _callerApp;
@@ -183,6 +184,7 @@ class BroadcastRecord extends Binder {
ordered = _serialized;
sticky = _sticky;
initialSticky = _initialSticky;
+ onlySendToCaller = _onlySendToCaller;
nextReceiver = 0;
state = IDLE;
}
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index 3ba3fbb..3a6492e 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -4,7 +4,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
@@ -12,7 +11,6 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
import android.app.ActivityManager;
@@ -24,6 +22,7 @@ import android.content.res.CompatibilityInfo;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index 0106114..5b3ff8d 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import android.app.IServiceConnection;
import android.app.PendingIntent;
+import android.content.Context;
import java.io.PrintWriter;
@@ -62,6 +63,33 @@ class ConnectionRecord {
sb.append("ConnectionRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' ');
+ if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+ sb.append("CR ");
+ }
+ if ((flags&Context.BIND_DEBUG_UNBIND) != 0) {
+ sb.append("DBG ");
+ }
+ if ((flags&Context.BIND_NOT_FOREGROUND) != 0) {
+ sb.append("NOTFG ");
+ }
+ if ((flags&Context.BIND_ABOVE_CLIENT) != 0) {
+ sb.append("ABCLT ");
+ }
+ if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+ sb.append("OOM ");
+ }
+ if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) {
+ sb.append("WPRI ");
+ }
+ if ((flags&Context.BIND_IMPORTANT) != 0) {
+ sb.append("IMP ");
+ }
+ if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+ sb.append("ACT ");
+ }
+ if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
+ sb.append("NOTVIS ");
+ }
if (serviceDead) {
sb.append("DEAD ");
}
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index fb21b06..c80d63a 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -38,6 +38,7 @@ class ContentProviderRecord {
final int uid;
final ApplicationInfo appInfo;
final ComponentName name;
+ final boolean singleton;
public IContentProvider provider;
public boolean noReleaseNeeded;
// All attached clients
@@ -54,12 +55,13 @@ class ContentProviderRecord {
String shortStringName;
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
- ApplicationInfo ai, ComponentName _name) {
+ ApplicationInfo ai, ComponentName _name, boolean _singleton) {
service = _service;
info = _info;
uid = ai.uid;
appInfo = ai;
name = _name;
+ singleton = _singleton;
noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
}
@@ -69,6 +71,7 @@ class ContentProviderRecord {
uid = cpr.uid;
appInfo = cpr.appInfo;
name = cpr.name;
+ singleton = cpr.singleton;
noReleaseNeeded = cpr.noReleaseNeeded;
}
@@ -150,6 +153,9 @@ class ContentProviderRecord {
pw.print(prefix); pw.print("uid="); pw.print(uid);
pw.print(" provider="); pw.println(provider);
}
+ if (singleton) {
+ pw.print(prefix); pw.print("singleton="); pw.println(singleton);
+ }
pw.print(prefix); pw.print("authority="); pw.println(info.authority);
if (full) {
if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java
index c94f714..0a92964 100644
--- a/services/java/com/android/server/am/IntentBindRecord.java
+++ b/services/java/com/android/server/am/IntentBindRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
@@ -78,6 +79,20 @@ class IntentBindRecord {
intent = _intent;
}
+ int collectFlags() {
+ int flags = 0;
+ if (apps.size() > 0) {
+ for (AppBindRecord app : apps.values()) {
+ if (app.connections.size() > 0) {
+ for (ConnectionRecord conn : app.connections) {
+ flags |= conn.flags;
+ }
+ }
+ }
+ }
+ return flags;
+ }
+
public String toString() {
if (stringName != null) {
return stringName;
@@ -86,6 +101,9 @@ class IntentBindRecord {
sb.append("IntentBindRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' ');
+ if ((collectFlags()&Context.BIND_AUTO_CREATE) != 0) {
+ sb.append("CR ");
+ }
sb.append(service.shortName);
sb.append(':');
if (intent != null) {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index ad15da1..d3b8510 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -25,7 +25,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import java.io.PrintWriter;
@@ -259,7 +259,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false, UserId
+ requiredPermission, (finishedReceiver != null), false, UserHandle
.getUserId(uid));
sendFinish = false;
} catch (RuntimeException e) {
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index af7b314..afc060e 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -24,6 +24,7 @@ import com.android.server.wm.WindowManagerService;
import android.graphics.Point;
import android.util.Slog;
+import android.view.Display;
/**
* Activity manager code dealing with processes.
@@ -146,7 +147,7 @@ class ProcessList {
void applyDisplaySize(WindowManagerService wm) {
if (!mHaveDisplaySize) {
Point p = new Point();
- wm.getInitialDisplaySize(p);
+ wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, p);
if (p.x != 0 && p.y != 0) {
updateOomLevels(p.x, p.y, true);
mHaveDisplaySize = true;
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index cba9480..d372422 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -30,7 +30,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.TimeUtils;
@@ -61,6 +61,7 @@ class ProcessRecord {
long lruWeight; // Weight for ordering in LRU list
int maxAdj; // Maximum OOM adjustment for this process
int hiddenAdj; // If hidden, this is the adjustment to use
+ int emptyAdj; // If empty, this is the adjustment to use
int curRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
int nonStoppingAdj; // Adjustment not counting any stopping activities
@@ -73,6 +74,7 @@ class ProcessRecord {
boolean serviceb; // Process currently is on the service B list
boolean keeping; // Actively running code so don't kill due to that?
boolean setIsForeground; // Running foreground UI when last set?
+ boolean hasActivities; // Are there any activities running in this process?
boolean foregroundServices; // Running any services that are foreground?
boolean foregroundActivities; // Running any activities that are foreground?
boolean systemNoUi; // This is a system process, but not currently showing UI.
@@ -199,6 +201,7 @@ class ProcessRecord {
pw.print(" empty="); pw.println(empty);
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
pw.print(" hidden="); pw.print(hiddenAdj);
+ pw.print(" empty="); pw.print(emptyAdj);
pw.print(" curRaw="); pw.print(curRawAdj);
pw.print(" setRaw="); pw.print(setRawAdj);
pw.print(" nonStopping="); pw.print(nonStoppingAdj);
@@ -215,7 +218,9 @@ class ProcessRecord {
pw.print(" foregroundServices="); pw.print(foregroundServices);
pw.print(" forcingToForeground="); pw.println(forcingToForeground);
pw.print(prefix); pw.print("persistent="); pw.print(persistent);
- pw.print(" removed="); pw.println(removed);
+ pw.print(" removed="); pw.print(removed);
+ pw.print(" hasActivities="); pw.print(hasActivities);
+ pw.print(" foregroundActivities="); pw.println(foregroundActivities);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
if (!keeping) {
@@ -308,12 +313,12 @@ class ProcessRecord {
info = _info;
isolated = _info.uid != _uid;
uid = _uid;
- userId = UserId.getUserId(_uid);
+ userId = UserHandle.getUserId(_uid);
processName = _processName;
pkgList.add(_info.packageName);
thread = _thread;
maxAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
- hiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+ hiddenAdj = emptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
curRawAdj = setRawAdj = -100;
curAdj = setAdj = -100;
persistent = false;
@@ -391,7 +396,7 @@ class ProcessRecord {
sb.append(info.uid%Process.FIRST_APPLICATION_UID);
if (uid != info.uid) {
sb.append('i');
- sb.append(UserId.getAppId(uid) - Process.FIRST_ISOLATED_UID);
+ sb.append(UserHandle.getAppId(uid) - Process.FIRST_ISOLATED_UID);
}
}
}
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index e4608a2..15fbb98 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -20,7 +20,7 @@ import android.content.ComponentName;
import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
@@ -44,9 +44,9 @@ public class ProviderMap {
private static final boolean DBG = false;
- private final HashMap<String, ContentProviderRecord> mGlobalByName
+ private final HashMap<String, ContentProviderRecord> mSingletonByName
= new HashMap<String, ContentProviderRecord>();
- private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
+ private final HashMap<ComponentName, ContentProviderRecord> mSingletonByClass
= new HashMap<ComponentName, ContentProviderRecord>();
private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
@@ -63,7 +63,7 @@ public class ProviderMap {
Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
}
// Try to find it in the global list
- ContentProviderRecord record = mGlobalByName.get(name);
+ ContentProviderRecord record = mSingletonByName.get(name);
if (record != null) {
return record;
}
@@ -81,7 +81,7 @@ public class ProviderMap {
Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
}
// Try to find it in the global list
- ContentProviderRecord record = mGlobalByClass.get(name);
+ ContentProviderRecord record = mSingletonByClass.get(name);
if (record != null) {
return record;
}
@@ -95,10 +95,10 @@ public class ProviderMap {
Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
+ ", record uid = " + record.appInfo.uid);
}
- if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
- mGlobalByName.put(name, record);
+ if (record.singleton) {
+ mSingletonByName.put(name, record);
} else {
- final int userId = UserId.getUserId(record.appInfo.uid);
+ final int userId = UserHandle.getUserId(record.appInfo.uid);
getProvidersByName(userId).put(name, record);
}
}
@@ -108,19 +108,19 @@ public class ProviderMap {
Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
+ ", record uid = " + record.appInfo.uid);
}
- if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
- mGlobalByClass.put(name, record);
+ if (record.singleton) {
+ mSingletonByClass.put(name, record);
} else {
- final int userId = UserId.getUserId(record.appInfo.uid);
+ final int userId = UserHandle.getUserId(record.appInfo.uid);
getProvidersByClass(userId).put(name, record);
}
}
void removeProviderByName(String name, int optionalUserId) {
- if (mGlobalByName.containsKey(name)) {
+ if (mSingletonByName.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByName name=" + name);
- mGlobalByName.remove(name);
+ mSingletonByName.remove(name);
} else {
// TODO: Verify this works, i.e., the caller happens to be from the correct user
if (DBG)
@@ -137,10 +137,10 @@ public class ProviderMap {
}
void removeProviderByClass(ComponentName name, int optionalUserId) {
- if (mGlobalByClass.containsKey(name)) {
+ if (mSingletonByClass.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByClass name=" + name);
- mGlobalByClass.remove(name);
+ mSingletonByClass.remove(name);
} else {
if (DBG)
Slog.i(TAG,
@@ -207,37 +207,29 @@ public class ProviderMap {
}
void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
- boolean needSep = false;
- if (mGlobalByClass.size() > 0) {
- if (needSep)
- pw.println(" ");
- pw.println(" Published content providers (by class):");
- dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
+ if (mSingletonByClass.size() > 0) {
+ pw.println(" Published single-user content providers (by class):");
+ dumpProvidersByClassLocked(pw, dumpAll, mSingletonByClass);
}
- if (mProvidersByClassPerUser.size() > 1) {
+ pw.println("");
+ for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
+ HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
pw.println("");
- for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
- HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
- pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
- dumpProvidersByClassLocked(pw, dumpAll, map);
- pw.println(" ");
- }
- } else if (mProvidersByClassPerUser.size() == 1) {
- HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0);
+ pw.println(" Published user " + mProvidersByClassPerUser.keyAt(i)
+ + " content providers (by class):");
dumpProvidersByClassLocked(pw, dumpAll, map);
}
- needSep = true;
if (dumpAll) {
- pw.println(" ");
- pw.println(" Authority to provider mappings:");
- dumpProvidersByNameLocked(pw, mGlobalByName);
+ pw.println("");
+ pw.println(" Single-user authority to provider mappings:");
+ dumpProvidersByNameLocked(pw, mSingletonByName);
for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
- if (i > 0) {
- pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + ":");
- }
+ pw.println("");
+ pw.println(" User " + mProvidersByNamePerUser.keyAt(i)
+ + " authority to provider mappings:");
dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
}
}
@@ -338,6 +330,4 @@ public class ProviderMap {
}
}
}
-
-
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 828eef7..5d60b9c 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -23,6 +23,7 @@ import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -31,7 +32,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.TimeUtils;
@@ -260,6 +261,9 @@ class ServiceRecord extends Binder {
IntentBindRecord b = it.next();
pw.print(prefix); pw.print("* IntentBindRecord{");
pw.print(Integer.toHexString(System.identityHashCode(b)));
+ if ((b.collectFlags()&Context.BIND_AUTO_CREATE) != 0) {
+ pw.append(" CREATE");
+ }
pw.println("}:");
b.dumpInService(pw, prefix + " ");
}
@@ -296,7 +300,7 @@ class ServiceRecord extends Binder {
this.restarter = restarter;
createTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
- userId = UserId.getUserId(appInfo.uid);
+ userId = UserHandle.getUserId(appInfo.uid);
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 3a767c2..1bae9ca 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,7 @@ package com.android.server.am;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import java.io.PrintWriter;
@@ -101,7 +101,7 @@ class TaskRecord extends ThumbnailHolder {
}
if (info.applicationInfo != null) {
- userId = UserId.getUserId(info.applicationInfo.uid);
+ userId = UserHandle.getUserId(info.applicationInfo.uid);
}
}
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index ba65f39..7059674 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -27,12 +27,12 @@ import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.app.IUsageStats;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.AtomicFile;
import com.android.internal.os.PkgUsageStats;
import com.android.internal.util.FastXmlSerializer;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 682ecf8..e4f1f7a 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -319,6 +319,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void limitReached(String limitName, String iface) {}
+ public void interfaceClassDataActivityChanged(String label, boolean active) {}
+
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
@@ -501,8 +503,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mUsbTetherRequested = false;
}
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+ NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ if (networkInfo != null &&
+ networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) {
+ if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+ }
}
}
}
@@ -1135,7 +1142,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private State mStopTetheringErrorState;
private State mSetDnsForwardersErrorState;
- private ArrayList mNotifyList;
+ private ArrayList<TetherInterfaceSM> mNotifyList;
private int mCurrentConnectionSequence;
private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
@@ -1165,7 +1172,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mSetDnsForwardersErrorState = new SetDnsForwardersErrorState();
addState(mSetDnsForwardersErrorState);
- mNotifyList = new ArrayList();
+ mNotifyList = new ArrayList<TetherInterfaceSM>();
setInitialState(mInitialState);
}
@@ -1362,8 +1369,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
mUpstreamIfaceName = ifaceName;
- for (Object o : mNotifyList) {
- TetherInterfaceSM sm = (TetherInterfaceSM)o;
+ for (TetherInterfaceSM sm : mNotifyList) {
sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
ifaceName);
}
@@ -1381,13 +1387,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode requested by " + who.toString());
+ if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
mNotifyList.add(who);
transitionTo(mTetherModeAliveState);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who.toString());
+ if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
int index = mNotifyList.indexOf(who);
if (index != -1) {
mNotifyList.remove(who);
@@ -1424,18 +1430,29 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
+ if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
mNotifyList.add(who);
who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
mUpstreamIfaceName);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
+ if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
int index = mNotifyList.indexOf(who);
if (index != -1) {
+ if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who);
mNotifyList.remove(index);
if (mNotifyList.isEmpty()) {
turnOffMasterTetherSettings(); // transitions appropriately
+ } else {
+ if (DBG) {
+ Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
+ " live requests:");
+ for (Object o : mNotifyList) Log.d(TAG, " " + o);
+ }
}
+ } else {
+ Log.e(TAG, "TetherModeAliveState UNREQUESTED has unknown who: " + who);
}
break;
case CMD_UPSTREAM_CHANGED:
@@ -1562,7 +1579,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
pw.println();
pw.println("Tether state:");
for (Object o : mIfaces.values()) {
- pw.println(" "+o.toString());
+ pw.println(" " + o);
}
}
pw.println();
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 4b82037..b3cbb84 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -16,8 +16,11 @@
package com.android.server.connectivity;
+import static android.Manifest.permission.BIND_VPN_SERVICE;
+
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -25,55 +28,116 @@ import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.net.BaseNetworkStateTracker;
+import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
+import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.net.NetworkInfo;
+import android.net.RouteInfo;
+import android.net.NetworkInfo.DetailedState;
import android.os.Binder;
import android.os.FileUtils;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
+import android.os.SystemService;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.util.Log;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.Preconditions;
import com.android.server.ConnectivityService.VpnCallback;
+import com.android.server.net.BaseNetworkObserver;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.Inet4Address;
+import java.net.InetAddress;
import java.nio.charset.Charsets;
import java.util.Arrays;
+import libcore.io.IoUtils;
+
/**
* @hide
*/
-public class Vpn extends INetworkManagementEventObserver.Stub {
-
- private final static String TAG = "Vpn";
-
- private final static String BIND_VPN_SERVICE =
- android.Manifest.permission.BIND_VPN_SERVICE;
+public class Vpn extends BaseNetworkStateTracker {
+ private static final String TAG = "Vpn";
+ private static final boolean LOGD = true;
+
+ // TODO: create separate trackers for each unique VPN to support
+ // automated reconnection
- private final Context mContext;
private final VpnCallback mCallback;
private String mPackage = VpnConfig.LEGACY_VPN;
private String mInterface;
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
+ private PendingIntent mStatusIntent;
+ private boolean mEnableNotif = true;
- public Vpn(Context context, VpnCallback callback) {
+ public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) {
+ // TODO: create dedicated TYPE_VPN network type
+ super(ConnectivityManager.TYPE_DUMMY);
mContext = context;
mCallback = callback;
+
+ try {
+ netService.registerObserver(mObserver);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Problem registering observer", e);
+ }
+ }
+
+ public void setEnableNotifications(boolean enableNotif) {
+ mEnableNotif = enableNotif;
+ }
+
+ @Override
+ protected void startMonitoringInternal() {
+ // Ignored; events are sent through callbacks for now
+ }
+
+ @Override
+ public boolean teardown() {
+ // TODO: finish migration to unique tracker for each VPN
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean reconnect() {
+ // TODO: finish migration to unique tracker for each VPN
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getTcpBufferSizesPropName() {
+ return PROP_TCP_BUFFER_UNKNOWN;
+ }
+
+ /**
+ * Update current state, dispaching event to listeners.
+ */
+ private void updateState(DetailedState detailedState, String reason) {
+ if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
+ mNetworkInfo.setDetailedState(detailedState, reason, null);
+ mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
}
/**
@@ -112,10 +176,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Reset the interface and hide the notification.
if (mInterface != null) {
jniReset(mInterface);
- long identity = Binder.clearCallingIdentity();
- mCallback.restore();
- hideNotification();
- Binder.restoreCallingIdentity(identity);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.restore();
+ hideNotification();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mInterface = null;
}
@@ -136,6 +203,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
mPackage = newPackage;
+ updateState(DetailedState.IDLE, "prepare");
return true;
}
@@ -144,7 +212,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
* interface. The socket is NOT closed by this method.
*
* @param socket The socket to be bound.
- * @param name The name of the interface.
+ * @param interfaze The name of the interface.
*/
public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
PackageManager pm = mContext.getPackageManager();
@@ -208,6 +276,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
try {
+ updateState(DetailedState.CONNECTING, "establish");
String interfaze = jniGetName(tun.getFd());
if (jniSetAddresses(interfaze, config.addresses) < 1) {
throw new IllegalArgumentException("At least one address must be specified");
@@ -228,11 +297,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
mConnection = connection;
mInterface = interfaze;
} catch (RuntimeException e) {
- try {
- tun.close();
- } catch (Exception ex) {
- // ignore
- }
+ updateState(DetailedState.FAILED, "establish");
+ IoUtils.closeQuietly(tun);
throw e;
}
Log.i(TAG, "Established by " + config.user + " on " + mInterface);
@@ -242,54 +308,61 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
config.interfaze = mInterface;
// Override DNS servers and show the notification.
- long identity = Binder.clearCallingIdentity();
- mCallback.override(config.dnsServers, config.searchDomains);
- showNotification(config, label, bitmap);
- Binder.restoreCallingIdentity(identity);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.override(config.dnsServers, config.searchDomains);
+ showNotification(config, label, bitmap);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ // TODO: ensure that contract class eventually marks as connected
+ updateState(DetailedState.AUTHENTICATING, "establish");
return tun;
}
- // INetworkManagementEventObserver.Stub
- @Override
- public void interfaceAdded(String interfaze) {
- }
-
- // INetworkManagementEventObserver.Stub
- @Override
- public synchronized void interfaceStatusChanged(String interfaze, boolean up) {
- if (!up && mLegacyVpnRunner != null) {
- mLegacyVpnRunner.check(interfaze);
+ @Deprecated
+ public synchronized void interfaceStatusChanged(String iface, boolean up) {
+ try {
+ mObserver.interfaceStatusChanged(iface, up);
+ } catch (RemoteException e) {
+ // ignored; target is local
}
}
- // INetworkManagementEventObserver.Stub
- @Override
- public void interfaceLinkStateChanged(String interfaze, boolean up) {
- }
-
- // INetworkManagementEventObserver.Stub
- @Override
- public synchronized void interfaceRemoved(String interfaze) {
- if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
- long identity = Binder.clearCallingIdentity();
- mCallback.restore();
- hideNotification();
- Binder.restoreCallingIdentity(identity);
- mInterface = null;
- if (mConnection != null) {
- mContext.unbindService(mConnection);
- mConnection = null;
- } else if (mLegacyVpnRunner != null) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
+ private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
+ @Override
+ public void interfaceStatusChanged(String interfaze, boolean up) {
+ synchronized (Vpn.this) {
+ if (!up && mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.check(interfaze);
+ }
}
}
- }
- // INetworkManagementEventObserver.Stub
- @Override
- public void limitReached(String limit, String interfaze) {
- }
+ @Override
+ public void interfaceRemoved(String interfaze) {
+ synchronized (Vpn.this) {
+ if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.restore();
+ hideNotification();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ mInterface = null;
+ if (mConnection != null) {
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
+ } else if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
+ }
+ }
+ }
+ }
+ };
private void enforceControlPermission() {
// System user is allowed to control VPN.
@@ -326,6 +399,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
}
private void showNotification(VpnConfig config, String label, Bitmap icon) {
+ if (!mEnableNotif) return;
+ mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
+
NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -341,15 +417,18 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
.setLargeIcon(icon)
.setContentTitle(title)
.setContentText(text)
- .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config))
+ .setContentIntent(mStatusIntent)
.setDefaults(0)
.setOngoing(true)
- .getNotification();
+ .build();
nm.notify(R.drawable.vpn_connected, notification);
}
}
private void hideNotification() {
+ if (!mEnableNotif) return;
+ mStatusIntent = null;
+
NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -366,31 +445,172 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
private native int jniCheck(String interfaze);
private native void jniProtect(int socket, String interfaze);
+ private static String findLegacyVpnGateway(LinkProperties prop) {
+ for (RouteInfo route : prop.getRoutes()) {
+ // Currently legacy VPN only works on IPv4.
+ if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
+ return route.getGateway().getHostAddress();
+ }
+ }
+
+ throw new IllegalStateException("Unable to find suitable gateway");
+ }
+
/**
- * Start legacy VPN. This method stops the daemons and restart them
- * if arguments are not null. Heavy things are offloaded to another
- * thread, so callers will not be blocked for a long time.
- *
- * @param config The parameters to configure the network.
- * @param raoocn The arguments to be passed to racoon.
- * @param mtpd The arguments to be passed to mtpd.
+ * Start legacy VPN, controlling native daemons as needed. Creates a
+ * secondary thread to perform connection work, returning quickly.
*/
- public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+ if (keyStore.state() != KeyStore.State.UNLOCKED) {
+ throw new IllegalStateException("KeyStore isn't unlocked");
+ }
+
+ final String iface = egress.getInterfaceName();
+ final String gateway = findLegacyVpnGateway(egress);
+
+ // Load certificates.
+ String privateKey = "";
+ String userCert = "";
+ String caCert = "";
+ String serverCert = "";
+ if (!profile.ipsecUserCert.isEmpty()) {
+ privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
+ byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
+ userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (!profile.ipsecCaCert.isEmpty()) {
+ byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
+ caCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (!profile.ipsecServerCert.isEmpty()) {
+ byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
+ serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
+ throw new IllegalStateException("Cannot load credentials");
+ }
+
+ // Prepare arguments for racoon.
+ String[] racoon = null;
+ switch (profile.type) {
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ racoon = new String[] {
+ iface, profile.server, "udppsk", profile.ipsecIdentifier,
+ profile.ipsecSecret, "1701",
+ };
+ break;
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ racoon = new String[] {
+ iface, profile.server, "udprsa", privateKey, userCert,
+ caCert, serverCert, "1701",
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+ racoon = new String[] {
+ iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
+ profile.ipsecSecret, profile.username, profile.password, "", gateway,
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+ racoon = new String[] {
+ iface, profile.server, "xauthrsa", privateKey, userCert,
+ caCert, serverCert, profile.username, profile.password, "", gateway,
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+ racoon = new String[] {
+ iface, profile.server, "hybridrsa",
+ caCert, serverCert, profile.username, profile.password, "", gateway,
+ };
+ break;
+ }
+
+ // Prepare arguments for mtpd.
+ String[] mtpd = null;
+ switch (profile.type) {
+ case VpnProfile.TYPE_PPTP:
+ mtpd = new String[] {
+ iface, "pptp", profile.server, "1723",
+ "name", profile.username, "password", profile.password,
+ "linkname", "vpn", "refuse-eap", "nodefaultroute",
+ "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ (profile.mppe ? "+mppe" : "nomppe"),
+ };
+ break;
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ mtpd = new String[] {
+ iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
+ "name", profile.username, "password", profile.password,
+ "linkname", "vpn", "refuse-eap", "nodefaultroute",
+ "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ };
+ break;
+ }
+
+ VpnConfig config = new VpnConfig();
+ config.legacy = true;
+ config.user = profile.key;
+ config.interfaze = iface;
+ config.session = profile.name;
+ config.routes = profile.routes;
+ if (!profile.dnsServers.isEmpty()) {
+ config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
+ }
+ if (!profile.searchDomains.isEmpty()) {
+ config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
+ }
+
+ startLegacyVpn(config, racoon, mtpd);
+ }
+
+ private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ stopLegacyVpn();
+
// Prepare for the new request. This also checks the caller.
prepare(null, VpnConfig.LEGACY_VPN);
+ updateState(DetailedState.CONNECTING, "startLegacyVpn");
// Start a new LegacyVpnRunner and we are done!
mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
mLegacyVpnRunner.start();
}
+ public synchronized void stopLegacyVpn() {
+ if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
+
+ synchronized (LegacyVpnRunner.TAG) {
+ // wait for old thread to completely finish before spinning up
+ // new instance, otherwise state updates can be out of order.
+ }
+ }
+ }
+
/**
* Return the information of the current ongoing legacy VPN.
*/
public synchronized LegacyVpnInfo getLegacyVpnInfo() {
// Check if the caller is authorized.
enforceControlPermission();
- return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
+ if (mLegacyVpnRunner == null) return null;
+
+ final LegacyVpnInfo info = new LegacyVpnInfo();
+ info.key = mLegacyVpnRunner.mConfig.user;
+ info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
+ if (mNetworkInfo.isConnected()) {
+ info.intent = mStatusIntent;
+ }
+ return info;
+ }
+
+ public VpnConfig getLegacyVpnConfig() {
+ if (mLegacyVpnRunner != null) {
+ return mLegacyVpnRunner.mConfig;
+ } else {
+ return null;
+ }
}
/**
@@ -407,8 +627,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
private final String[] mDaemons;
private final String[][] mArguments;
private final LocalSocket[] mSockets;
- private final String mOuterInterface;
- private final LegacyVpnInfo mInfo;
private long mTimer = -1;
@@ -416,20 +634,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
super(TAG);
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
+ // TODO: clear arguments from memory once launched
mArguments = new String[][] {racoon, mtpd};
mSockets = new LocalSocket[mDaemons.length];
- mInfo = new LegacyVpnInfo();
-
- // This is the interface which VPN is running on.
- mOuterInterface = mConfig.interfaze;
-
- // Legacy VPN is not a real package, so we use it to carry the key.
- mInfo.key = mConfig.user;
- mConfig.user = VpnConfig.LEGACY_VPN;
}
public void check(String interfaze) {
- if (interfaze.equals(mOuterInterface)) {
+ if (interfaze.equals(mConfig.interfaze)) {
Log.i(TAG, "Legacy VPN is going down with " + interfaze);
exit();
}
@@ -439,21 +650,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// We assume that everything is reset after stopping the daemons.
interrupt();
for (LocalSocket socket : mSockets) {
- try {
- socket.close();
- } catch (Exception e) {
- // ignore
- }
- }
- }
-
- public LegacyVpnInfo getInfo() {
- // Update the info when VPN is disconnected.
- if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) {
- mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
- mInfo.intent = null;
+ IoUtils.closeQuietly(socket);
}
- return mInfo;
+ updateState(DetailedState.DISCONNECTED, "exit");
}
@Override
@@ -463,6 +662,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
synchronized (TAG) {
Log.v(TAG, "Executing");
execute();
+ monitorDaemons();
}
}
@@ -474,22 +674,21 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
} else if (now - mTimer <= 60000) {
Thread.sleep(yield ? 200 : 1);
} else {
- mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
+ updateState(DetailedState.FAILED, "checkpoint");
throw new IllegalStateException("Time is up");
}
}
private void execute() {
// Catch all exceptions so we can clean up few things.
+ boolean initFinished = false;
try {
// Initialize the timer.
checkpoint(false);
- mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
// Wait for the daemons to stop.
for (String daemon : mDaemons) {
- String key = "init.svc." + daemon;
- while (!"stopped".equals(SystemProperties.get(key, "stopped"))) {
+ while (!SystemService.isStopped(daemon)) {
checkpoint(true);
}
}
@@ -501,6 +700,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
throw new IllegalStateException("Cannot delete the state");
}
new File("/data/misc/vpn/abort").delete();
+ initFinished = true;
// Check if we need to restart any of the daemons.
boolean restart = false;
@@ -508,10 +708,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
restart = restart || (arguments != null);
}
if (!restart) {
- mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
+ updateState(DetailedState.DISCONNECTED, "execute");
return;
}
- mInfo.state = LegacyVpnInfo.STATE_CONNECTING;
+ updateState(DetailedState.CONNECTING, "execute");
// Start the daemon with arguments.
for (int i = 0; i < mDaemons.length; ++i) {
@@ -522,11 +722,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Start the daemon.
String daemon = mDaemons[i];
- SystemProperties.set("ctl.start", daemon);
+ SystemService.start(daemon);
// Wait for the daemon to start.
- String key = "init.svc." + daemon;
- while (!"running".equals(SystemProperties.get(key))) {
+ while (!SystemService.isRunning(daemon)) {
checkpoint(true);
}
@@ -582,8 +781,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
// Check if a running daemon is dead.
for (int i = 0; i < mDaemons.length; ++i) {
String daemon = mDaemons[i];
- if (mArguments[i] != null && !"running".equals(
- SystemProperties.get("init.svc." + daemon))) {
+ if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
throw new IllegalStateException(daemon + " is dead");
}
}
@@ -640,26 +838,53 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
showNotification(mConfig, null, null);
Log.i(TAG, "Connected!");
- mInfo.state = LegacyVpnInfo.STATE_CONNECTED;
- mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null);
+ updateState(DetailedState.CONNECTED, "execute");
}
} catch (Exception e) {
Log.i(TAG, "Aborting", e);
exit();
} finally {
// Kill the daemons if they fail to stop.
- if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
+ if (!initFinished) {
for (String daemon : mDaemons) {
- SystemProperties.set("ctl.stop", daemon);
+ SystemService.stop(daemon);
}
}
// Do not leave an unstable state.
- if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
- mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
- mInfo.state = LegacyVpnInfo.STATE_FAILED;
+ if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
+ updateState(DetailedState.FAILED, "execute");
}
}
}
+
+ /**
+ * Monitor the daemons we started, moving to disconnected state if the
+ * underlying services fail.
+ */
+ private void monitorDaemons() {
+ if (!mNetworkInfo.isConnected()) {
+ return;
+ }
+
+ try {
+ while (true) {
+ Thread.sleep(2000);
+ for (int i = 0; i < mDaemons.length; i++) {
+ if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
+ return;
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
+ } finally {
+ for (String daemon : mDaemons) {
+ SystemService.stop(daemon);
+ }
+
+ updateState(DetailedState.DISCONNECTED, "babysit");
+ }
+ }
}
}
diff --git a/services/java/com/android/server/display/DisplayAdapter.java b/services/java/com/android/server/display/DisplayAdapter.java
new file mode 100644
index 0000000..f9fa7a8
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+/**
+ * A display adapter makes zero or more display devices available to the system
+ * and provides facilities for discovering when displays are connected or disconnected.
+ * <p>
+ * For now, all display adapters are registered in the system server but
+ * in principle it could be done from other processes.
+ * </p>
+ */
+public abstract class DisplayAdapter {
+ /**
+ * Gets the display adapter name for debugging purposes.
+ *
+ * @return The display adapter name.
+ */
+ public abstract String getName();
+
+ /**
+ * Registers the display adapter with the display manager.
+ * The display adapter should register any built-in display devices now.
+ * Other display devices can be registered dynamically later.
+ *
+ * @param listener The listener for callbacks.
+ */
+ public abstract void register(Listener listener);
+
+ public interface Listener {
+ public void onDisplayDeviceAdded(DisplayDevice device);
+ public void onDisplayDeviceRemoved(DisplayDevice device);
+ }
+}
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
new file mode 100644
index 0000000..57002ff
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+/**
+ * Represents a physical display device such as the built-in display
+ * an external monitor, or a WiFi display.
+ */
+public abstract class DisplayDevice {
+ /**
+ * Gets the display adapter that makes the display device available.
+ *
+ * @return The display adapter.
+ */
+ public abstract DisplayAdapter getAdapter();
+
+ /**
+ * Gets information about the display device.
+ *
+ * @param outInfo The object to populate with the information.
+ */
+ public abstract void getInfo(DisplayDeviceInfo outInfo);
+}
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
new file mode 100644
index 0000000..9c0f964
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+/**
+ * Describes the characteristics of a physical display device.
+ */
+public final class DisplayDeviceInfo {
+ /**
+ * Gets the name of the display device, which may be derived from
+ * EDID or other sources. The name may be displayed to the user.
+ */
+ public String name;
+
+ /**
+ * The width of the display in its natural orientation, in pixels.
+ * This value is not affected by display rotation.
+ */
+ public int width;
+
+ /**
+ * The height of the display in its natural orientation, in pixels.
+ * This value is not affected by display rotation.
+ */
+ public int height;
+
+ public float refreshRate;
+ public int densityDpi;
+ public float xDpi;
+ public float yDpi;
+
+ public void copyFrom(DisplayDeviceInfo other) {
+ name = other.name;
+ width = other.width;
+ height = other.height;
+ refreshRate = other.refreshRate;
+ densityDpi = other.densityDpi;
+ xDpi = other.xDpi;
+ yDpi = other.yDpi;
+ }
+
+ // For debugging purposes
+ @Override
+ public String toString() {
+ return "\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
+ + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi";
+ }
+}
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
new file mode 100644
index 0000000..2ebad1d
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.IDisplayManager;
+import android.os.Binder;
+import android.os.SystemProperties;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Manages the properties, media routing and power state of attached displays.
+ * <p>
+ * The display manager service does not own or directly control the displays.
+ * Instead, other components in the system register their display adapters with the
+ * display manager service which acts as a central controller.
+ * </p>
+ */
+public final class DisplayManagerService extends IDisplayManager.Stub {
+ private static final String TAG = "DisplayManagerService";
+
+ private static final String SYSTEM_HEADLESS = "ro.config.headless";
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+ private final boolean mHeadless;
+
+ private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+
+ public DisplayManagerService(Context context) {
+ mContext = context;
+ mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
+
+ registerDefaultDisplayAdapter();
+ }
+
+ private void registerDefaultDisplayAdapter() {
+ if (mHeadless) {
+ registerDisplayAdapter(new HeadlessDisplayAdapter(mContext));
+ } else {
+ registerDisplayAdapter(new SurfaceFlingerDisplayAdapter(mContext));
+ }
+ }
+
+ // FIXME: this isn't the right API for the long term
+ public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) {
+ // hardcoded assuming 720p touch screen plugged into HDMI and USB
+ // need to redesign this
+ info.width = 1280;
+ info.height = 720;
+ }
+
+ /**
+ * Returns true if the device is headless.
+ *
+ * @return True if the device is headless.
+ */
+ public boolean isHeadless() {
+ return mHeadless;
+ }
+
+ /**
+ * Save away new DisplayInfo data.
+ * @param displayId The local DisplayInfo to store the new data in.
+ * @param info The new data to be stored.
+ */
+ public void setDisplayInfo(int displayId, DisplayInfo info) {
+ synchronized (mLock) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ throw new UnsupportedOperationException();
+ }
+ mDefaultDisplayInfo.copyFrom(info);
+ }
+ }
+
+ /**
+ * Return requested DisplayInfo.
+ * @param displayId The data to retrieve.
+ * @param outInfo The structure to receive the data.
+ */
+ @Override // Binder call
+ public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) {
+ synchronized (mLock) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return false;
+ }
+ outInfo.copyFrom(mDefaultDisplayInfo);
+ return true;
+ }
+ }
+
+ private void registerDisplayAdapter(DisplayAdapter adapter) {
+ mDisplayAdapters.add(adapter);
+ adapter.register(new DisplayAdapter.Listener() {
+ @Override
+ public void onDisplayDeviceAdded(DisplayDevice device) {
+ DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+ device.getInfo(deviceInfo);
+ copyDisplayInfoFromDeviceInfo(mDefaultDisplayInfo, deviceInfo);
+ }
+
+ @Override
+ public void onDisplayDeviceRemoved(DisplayDevice device) {
+ }
+ });
+ }
+
+ private void copyDisplayInfoFromDeviceInfo(
+ DisplayInfo displayInfo, DisplayDeviceInfo deviceInfo) {
+ // Bootstrap the logical display using the physical display.
+ displayInfo.appWidth = deviceInfo.width;
+ displayInfo.appHeight = deviceInfo.height;
+ displayInfo.logicalWidth = deviceInfo.width;
+ displayInfo.logicalHeight = deviceInfo.height;
+ displayInfo.rotation = Surface.ROTATION_0;
+ displayInfo.refreshRate = deviceInfo.refreshRate;
+ displayInfo.logicalDensityDpi = deviceInfo.densityDpi;
+ displayInfo.physicalXDpi = deviceInfo.xDpi;
+ displayInfo.physicalYDpi = deviceInfo.yDpi;
+ displayInfo.smallestNominalAppWidth = deviceInfo.width;
+ displayInfo.smallestNominalAppHeight = deviceInfo.height;
+ displayInfo.largestNominalAppWidth = deviceInfo.width;
+ displayInfo.largestNominalAppHeight = deviceInfo.height;
+ }
+
+ @Override // Binder call
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext == null
+ || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump DisplayManager from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ pw.println("DISPLAY MANAGER (dumpsys display)\n");
+
+ pw.println("Headless: " + mHeadless);
+
+ synchronized (mLock) {
+ for (DisplayAdapter adapter : mDisplayAdapters) {
+ pw.println("Adapter: " + adapter.getName());
+ }
+
+ pw.println("Default display info: " + mDefaultDisplayInfo);
+ }
+
+ pw.println("Default display: "
+ + DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY));
+ }
+}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
new file mode 100644
index 0000000..17c2360
--- /dev/null
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+/**
+ * Provides a fake default display for headless systems.
+ */
+public final class HeadlessDisplayAdapter extends DisplayAdapter {
+ private final Context mContext;
+ private final HeadlessDisplayDevice mDefaultDisplayDevice;
+
+ public HeadlessDisplayAdapter(Context context) {
+ mContext = context;
+ mDefaultDisplayDevice = new HeadlessDisplayDevice();
+ }
+
+ @Override
+ public String getName() {
+ return "HeadlessDisplayAdapter";
+ }
+
+ @Override
+ public void register(Listener listener) {
+ listener.onDisplayDeviceAdded(mDefaultDisplayDevice);
+ }
+
+ private final class HeadlessDisplayDevice extends DisplayDevice {
+ @Override
+ public DisplayAdapter getAdapter() {
+ return HeadlessDisplayAdapter.this;
+ }
+
+ @Override
+ public void getInfo(DisplayDeviceInfo outInfo) {
+ outInfo.name = mContext.getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display);
+ outInfo.width = 640;
+ outInfo.height = 480;
+ outInfo.refreshRate = 60;
+ outInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+ outInfo.xDpi = 160;
+ outInfo.yDpi = 160;
+ }
+ }
+}
diff --git a/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java b/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java
new file mode 100644
index 0000000..9531acb
--- /dev/null
+++ b/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+
+/**
+ * A display adapter for the displays managed by Surface Flinger.
+ */
+public final class SurfaceFlingerDisplayAdapter extends DisplayAdapter {
+ private final Context mContext;
+ private final SurfaceFlingerDisplayDevice mDefaultDisplayDevice;
+
+ private static native void nativeGetDefaultDisplayDeviceInfo(DisplayDeviceInfo outInfo);
+
+ public SurfaceFlingerDisplayAdapter(Context context) {
+ mContext = context;
+ mDefaultDisplayDevice = new SurfaceFlingerDisplayDevice();
+ }
+
+ @Override
+ public String getName() {
+ return "SurfaceFlingerDisplayAdapter";
+ }
+
+ @Override
+ public void register(Listener listener) {
+ listener.onDisplayDeviceAdded(mDefaultDisplayDevice);
+ }
+
+ private final class SurfaceFlingerDisplayDevice extends DisplayDevice {
+ @Override
+ public DisplayAdapter getAdapter() {
+ return SurfaceFlingerDisplayAdapter.this;
+ }
+
+ @Override
+ public void getInfo(DisplayDeviceInfo outInfo) {
+ outInfo.name = mContext.getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display);
+ nativeGetDefaultDisplayDeviceInfo(outInfo);
+ }
+ }
+}
diff --git a/services/java/com/android/server/input/InputFilter.java b/services/java/com/android/server/input/InputFilter.java
deleted file mode 100644
index 2ce0a02..0000000
--- a/services/java/com/android/server/input/InputFilter.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.input;
-
-import com.android.server.wm.WindowManagerService;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.view.InputEvent;
-import android.view.InputEventConsistencyVerifier;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.WindowManagerPolicy;
-
-/**
- * Filters input events before they are dispatched to the system.
- * <p>
- * At most one input filter can be installed by calling
- * {@link WindowManagerService#setInputFilter}. When an input filter is installed, the
- * system's behavior changes as follows:
- * <ul>
- * <li>Input events are first delivered to the {@link WindowManagerPolicy}
- * interception methods before queuing as usual. This critical step takes care of managing
- * the power state of the device and handling wake keys.</li>
- * <li>Input events are then asynchronously delivered to the input filter's
- * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
- * applications as usual. The input filter only receives input events that were
- * generated by input device; the input filter will not receive input events that were
- * injected into the system by other means, such as by instrumentation.</li>
- * <li>The input filter processes and optionally transforms the stream of events. For example,
- * it may transform a sequence of motion events representing an accessibility gesture into
- * a different sequence of motion events, key presses or other system-level interactions.
- * The input filter can send events to be dispatched by calling
- * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the
- * input event.</li>
- * </ul>
- * </p>
- * <h3>The importance of input event consistency</h3>
- * <p>
- * The input filter mechanism is very low-level. At a minimum, it needs to ensure that it
- * sends an internally consistent stream of input events to the dispatcher. There are
- * very important invariants to be maintained.
- * </p><p>
- * For example, if a key down is sent, a corresponding key up should also be sent eventually.
- * Likewise, for touch events, each pointer must individually go down with
- * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then
- * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP}
- * and the sequence of pointer ids used must be consistent throughout the gesture.
- * </p><p>
- * Sometimes a filter may wish to cancel a previously dispatched key or motion. It should
- * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly.
- * </p><p>
- * The input filter must take into account the fact that the input events coming from different
- * devices or even different sources all consist of distinct streams of input.
- * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
- * the source of the event and its semantics. There are be multiple sources of keys,
- * touches and other input: they must be kept separate.
- * </p>
- * <h3>Policy flags</h3>
- * <p>
- * Input events received from the dispatcher and sent to the dispatcher have policy flags
- * associated with them. Policy flags control some functions of the dispatcher.
- * </p><p>
- * The early policy interception decides whether an input event should be delivered
- * to applications or dropped. The policy indicates its decision by setting the
- * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} policy flag. The input filter may
- * sometimes receive events that do not have this flag set. It should take note of
- * the fact that the policy intends to drop the event, clean up its state, and
- * then send appropriate cancellation events to the dispatcher if needed.
- * </p><p>
- * For example, suppose the input filter is processing a gesture and one of the touch events
- * it receives does not have the {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag set.
- * The input filter should clear its internal state about the gesture and then send key or
- * motion events to the dispatcher to cancel any keys or pointers that are down.
- * </p><p>
- * Corollary: Events that set sent to the dispatcher should usually include the
- * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped!
- * </p><p>
- * It may be prudent to disable automatic key repeating for synthetic key events
- * by setting the {@link WindowManagerPolicy#FLAG_DISABLE_KEY_REPEAT} policy flag.
- * </p>
- */
-public abstract class InputFilter {
- private static final int MSG_INSTALL = 1;
- private static final int MSG_UNINSTALL = 2;
- private static final int MSG_INPUT_EVENT = 3;
-
- private final H mH;
- private Host mHost;
-
- // Consistency verifiers for debugging purposes.
- private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier =
- InputEventConsistencyVerifier.isInstrumentationEnabled() ?
- new InputEventConsistencyVerifier(this,
- InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
- "InputFilter#InboundInputEventConsistencyVerifier") : null;
- private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier =
- InputEventConsistencyVerifier.isInstrumentationEnabled() ?
- new InputEventConsistencyVerifier(this,
- InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
- "InputFilter#OutboundInputEventConsistencyVerifier") : null;
-
- /**
- * Creates the input filter.
- *
- * @param looper The looper to run callbacks on.
- */
- public InputFilter(Looper looper) {
- mH = new H(looper);
- }
-
- /**
- * Called when the input filter is installed.
- * This method is guaranteed to be non-reentrant.
- *
- * @param host The input filter host environment.
- */
- final void install(Host host) {
- mH.obtainMessage(MSG_INSTALL, host).sendToTarget();
- }
-
- /**
- * Called when the input filter is uninstalled.
- * This method is guaranteed to be non-reentrant.
- */
- final void uninstall() {
- mH.obtainMessage(MSG_UNINSTALL).sendToTarget();
- }
-
- /**
- * Called to enqueue the input event for filtering.
- * The event will be recycled after the input filter processes it.
- * This method is guaranteed to be non-reentrant.
- *
- * @param event The input event to enqueue.
- */
- final void filterInputEvent(InputEvent event, int policyFlags) {
- mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget();
- }
-
- /**
- * Sends an input event to the dispatcher.
- *
- * @param event The input event to publish.
- * @param policyFlags The input event policy flags.
- */
- public void sendInputEvent(InputEvent event, int policyFlags) {
- if (event == null) {
- throw new IllegalArgumentException("event must not be null");
- }
- if (mHost == null) {
- throw new IllegalStateException("Cannot send input event because the input filter " +
- "is not installed.");
- }
- if (mOutboundInputEventConsistencyVerifier != null) {
- mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0);
- }
- mHost.sendInputEvent(event, policyFlags);
- }
-
- /**
- * Called when an input event has been received from the dispatcher.
- * <p>
- * The default implementation sends the input event back to the dispatcher, unchanged.
- * </p><p>
- * The event will be recycled when this method returns. If you want to keep it around,
- * make a copy!
- * </p>
- *
- * @param event The input event that was received.
- * @param policyFlags The input event policy flags.
- */
- public void onInputEvent(InputEvent event, int policyFlags) {
- sendInputEvent(event, policyFlags);
- }
-
- /**
- * Called when the filter is installed into the dispatch pipeline.
- * <p>
- * This method is called before the input filter receives any input events.
- * The input filter should take this opportunity to prepare itself.
- * </p>
- */
- public void onInstalled() {
- }
-
- /**
- * Called when the filter is uninstalled from the dispatch pipeline.
- * <p>
- * This method is called after the input filter receives its last input event.
- * The input filter should take this opportunity to clean up.
- * </p>
- */
- public void onUninstalled() {
- }
-
- private final class H extends Handler {
- public H(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_INSTALL:
- mHost = (Host)msg.obj;
- if (mInboundInputEventConsistencyVerifier != null) {
- mInboundInputEventConsistencyVerifier.reset();
- }
- if (mOutboundInputEventConsistencyVerifier != null) {
- mOutboundInputEventConsistencyVerifier.reset();
- }
- onInstalled();
- break;
-
- case MSG_UNINSTALL:
- try {
- onUninstalled();
- } finally {
- mHost = null;
- }
- break;
-
- case MSG_INPUT_EVENT: {
- final InputEvent event = (InputEvent)msg.obj;
- try {
- if (mInboundInputEventConsistencyVerifier != null) {
- mInboundInputEventConsistencyVerifier.onInputEvent(event, 0);
- }
- onInputEvent(event, msg.arg1);
- } finally {
- event.recycle();
- }
- break;
- }
- }
- }
- }
-
- interface Host {
- public void sendInputEvent(InputEvent event, int policyFlags);
- }
-}
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index bdd0aa4..29c68eb 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -42,8 +42,8 @@ import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
-import android.hardware.input.IInputManager;
import android.hardware.input.IInputDevicesChangedListener;
+import android.hardware.input.IInputManager;
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
import android.os.Binder;
@@ -57,11 +57,12 @@ import android.os.Process;
import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
-import android.server.BluetoothService;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
+import android.view.IInputFilter;
+import android.view.IInputFilterHost;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -108,7 +109,6 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
private final Callbacks mCallbacks;
private final InputManagerHandler mHandler;
private boolean mSystemReady;
- private BluetoothService mBluetoothService;
private NotificationManager mNotificationManager;
// Persistent data store. Must be locked each time during use.
@@ -137,7 +137,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
- InputFilter mInputFilter; // guarded by mInputFilterLock
+ IInputFilter mInputFilter; // guarded by mInputFilterLock
InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
private static native int nativeInit(InputManagerService service,
@@ -236,11 +236,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
updateShowTouchesFromSettings();
}
- public void systemReady(BluetoothService bluetoothService) {
+ // TODO(BT) Pass in paramter for bluetooth system
+ public void systemReady() {
if (DEBUG) {
Slog.d(TAG, "System ready.");
}
- mBluetoothService = bluetoothService;
mNotificationManager = (NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
mSystemReady = true;
@@ -425,9 +425,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
*
* @param filter The input filter, or null to remove the current filter.
*/
- public void setInputFilter(InputFilter filter) {
+ public void setInputFilter(IInputFilter filter) {
synchronized (mInputFilterLock) {
- final InputFilter oldFilter = mInputFilter;
+ final IInputFilter oldFilter = mInputFilter;
if (oldFilter == filter) {
return; // nothing to do
}
@@ -436,13 +436,21 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
mInputFilter = null;
mInputFilterHost.disconnectLocked();
mInputFilterHost = null;
- oldFilter.uninstall();
+ try {
+ oldFilter.uninstall();
+ } catch (RemoteException re) {
+ /* ignore */
+ }
}
if (filter != null) {
mInputFilter = filter;
mInputFilterHost = new InputFilterHost();
- filter.install(mInputFilterHost);
+ try {
+ filter.install(mInputFilterHost);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
}
nativeSetInputFilterEnabled(mPtr, filter != null);
@@ -1210,8 +1218,12 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
}
// Native callback.
- private void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
- mCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+ private void notifySwitch(long whenNanos, int switchCode, int switchValue) {
+ switch (switchCode) {
+ case SW_LID:
+ mCallbacks.notifyLidSwitchChanged(whenNanos, switchValue == 0);
+ break;
+ }
}
// Native callback.
@@ -1229,7 +1241,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
final boolean filterInputEvent(InputEvent event, int policyFlags) {
synchronized (mInputFilterLock) {
if (mInputFilter != null) {
- mInputFilter.filterInputEvent(event, policyFlags);
+ try {
+ mInputFilter.filterInputEvent(event, policyFlags);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
return false;
}
}
@@ -1384,9 +1400,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
// Native callback.
private String getDeviceAlias(String uniqueId) {
- if (mBluetoothService != null &&
- BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
- return mBluetoothService.getRemoteAlias(uniqueId);
+ if (BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
+ // TODO(BT) mBluetoothService.getRemoteAlias(uniqueId)
+ return null;
}
return null;
}
@@ -1422,6 +1438,10 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
* Private handler for the input manager.
*/
private final class InputManagerHandler extends Handler {
+ public InputManagerHandler() {
+ super(true /*async*/);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -1447,7 +1467,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
/**
* Hosting interface for input filters to call back into the input manager.
*/
- private final class InputFilterHost implements InputFilter.Host {
+ private final class InputFilterHost extends IInputFilterHost.Stub {
private boolean mDisconnected;
public void disconnectLocked() {
diff --git a/services/java/com/android/server/input/InputWindowHandle.java b/services/java/com/android/server/input/InputWindowHandle.java
index 03d66af..ad4fdd1 100644
--- a/services/java/com/android/server/input/InputWindowHandle.java
+++ b/services/java/com/android/server/input/InputWindowHandle.java
@@ -87,12 +87,16 @@ public final class InputWindowHandle {
// Window input features.
public int inputFeatures;
+ // Display this input is on.
+ public final int displayId;
+
private native void nativeDispose();
public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
- Object windowState) {
+ Object windowState, int displayId) {
this.inputApplicationHandle = inputApplicationHandle;
this.windowState = windowState;
+ this.displayId = displayId;
}
@Override
diff --git a/services/java/com/android/server/input/PersistentDataStore.java b/services/java/com/android/server/input/PersistentDataStore.java
index fbe3e8b..71de776 100644
--- a/services/java/com/android/server/input/PersistentDataStore.java
+++ b/services/java/com/android/server/input/PersistentDataStore.java
@@ -16,7 +16,6 @@
package com.android.server.input;
-import com.android.internal.os.AtomicFile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -25,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 07f3125..7d030e9 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -16,92 +16,64 @@
package com.android.server.location;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.location.Address;
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
-import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
+import com.android.server.ServiceWatcher;
import java.util.List;
/**
- * A class for proxying IGeocodeProvider implementations.
- *
- * {@hide}
+ * Proxy for IGeocodeProvider implementations.
*/
public class GeocoderProxy {
-
private static final String TAG = "GeocoderProxy";
- public static final String SERVICE_ACTION =
- "com.android.location.service.GeocodeProvider";
+ private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
private final Context mContext;
- private final Intent mIntent;
- private final Object mMutex = new Object(); // synchronizes access to mServiceConnection
- private Connection mServiceConnection; // never null after ctor
-
- public GeocoderProxy(Context context, String packageName) {
- mContext = context;
- mIntent = new Intent(SERVICE_ACTION);
- reconnect(packageName);
- }
-
- /** Bind to service. Will reconnect if already connected */
- public void reconnect(String packageName) {
- synchronized (mMutex) {
- if (mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- }
- mServiceConnection = new Connection();
- mIntent.setPackage(packageName);
- mContext.bindService(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ private final ServiceWatcher mServiceWatcher;
+
+ public static GeocoderProxy createAndBind(Context context,
+ List<String> initialPackageNames) {
+ GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames);
+ if (proxy.bind()) {
+ return proxy;
+ } else {
+ return null;
}
}
- private class Connection implements ServiceConnection {
+ public GeocoderProxy(Context context, List<String> initialPackageNames) {
+ mContext = context;
- private IGeocodeProvider mProvider;
+ mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
+ null, null);
+ }
- public void onServiceConnected(ComponentName className, IBinder service) {
- synchronized (this) {
- mProvider = IGeocodeProvider.Stub.asInterface(service);
- }
- }
+ private boolean bind () {
+ return mServiceWatcher.start();
+ }
- public void onServiceDisconnected(ComponentName className) {
- synchronized (this) {
- mProvider = null;
- }
- }
+ private IGeocodeProvider getService() {
+ return IGeocodeProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
- public IGeocodeProvider getProvider() {
- synchronized (this) {
- return mProvider;
- }
- }
+ public String getConnectedPackageName() {
+ return mServiceWatcher.getBestPackageName();
}
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
+ IGeocodeProvider provider = getService();
if (provider != null) {
try {
- return provider.getFromLocation(latitude, longitude, maxResults,
- params, addrs);
+ return provider.getFromLocation(latitude, longitude, maxResults, params, addrs);
} catch (RemoteException e) {
- Log.e(TAG, "getFromLocation failed", e);
+ Log.w(TAG, e);
}
}
return "Service not Available";
@@ -111,19 +83,17 @@ public class GeocoderProxy {
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
+ IGeocodeProvider provider = getService();
if (provider != null) {
try {
return provider.getFromLocationName(locationName, lowerLeftLatitude,
lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
maxResults, params, addrs);
} catch (RemoteException e) {
- Log.e(TAG, "getFromLocationName failed", e);
+ Log.w(TAG, e);
}
}
return "Service not Available";
}
+
}
diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java
new file mode 100644
index 0000000..26d9c15
--- /dev/null
+++ b/services/java/com/android/server/location/GeofenceManager.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 20012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import android.Manifest.permission;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Geofence;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationRequest;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.server.LocationManagerService;
+
+public class GeofenceManager implements LocationListener, PendingIntent.OnFinished {
+ private static final String TAG = "GeofenceManager";
+ private static final boolean D = LocationManagerService.D;
+
+ /**
+ * Assume a maximum land speed, as a heuristic to throttle location updates.
+ * (Air travel should result in an airplane mode toggle which will
+ * force a new location update anyway).
+ */
+ private static final int MAX_SPEED_M_S = 100; // 360 km/hr (high speed train)
+
+ private final Context mContext;
+ private final LocationManager mLocationManager;
+ private final PowerManager.WakeLock mWakeLock;
+ private final Looper mLooper; // looper thread to take location updates on
+ private final LocationBlacklist mBlacklist;
+
+ private Object mLock = new Object();
+
+ // access to members below is synchronized on mLock
+ private Location mLastLocation;
+ private List<GeofenceState> mFences = new LinkedList<GeofenceState>();
+
+ public GeofenceManager(Context context, LocationBlacklist blacklist) {
+ mContext = context;
+ mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mLooper = Looper.myLooper();
+ mBlacklist = blacklist;
+
+ LocationRequest request = new LocationRequest()
+ .setQuality(LocationRequest.POWER_NONE)
+ .setFastestInterval(0);
+ mLocationManager.requestLocationUpdates(request, this, Looper.myLooper());
+ }
+
+ public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, int uid,
+ String packageName) {
+ GeofenceState state = new GeofenceState(geofence, mLastLocation,
+ request.getExpireAt(), packageName, intent);
+
+ synchronized (mLock) {
+ // first make sure it doesn't already exist
+ for (int i = mFences.size() - 1; i >= 0; i--) {
+ GeofenceState w = mFences.get(i);
+ if (geofence.equals(w.mFence) && intent.equals(w.mIntent)) {
+ // already exists, remove the old one
+ mFences.remove(i);
+ break;
+ }
+ }
+ mFences.add(state);
+ updateProviderRequirementsLocked();
+ }
+ }
+
+ public void removeFence(Geofence fence, PendingIntent intent) {
+ synchronized (mLock) {
+ Iterator<GeofenceState> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceState state = iter.next();
+ if (state.mIntent.equals(intent)) {
+
+ if (fence == null) {
+ // alwaus remove
+ iter.remove();
+ } else {
+ // just remove matching fences
+ if (fence.equals(state.mFence)) {
+ iter.remove();
+ }
+ }
+ }
+ }
+ updateProviderRequirementsLocked();
+ }
+ }
+
+ public void removeFence(String packageName) {
+ synchronized (mLock) {
+ Iterator<GeofenceState> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceState state = iter.next();
+ if (state.mPackageName.equals(packageName)) {
+ iter.remove();
+ }
+ }
+ updateProviderRequirementsLocked();
+ }
+ }
+
+ private void removeExpiredFencesLocked() {
+ long time = SystemClock.elapsedRealtime();
+ Iterator<GeofenceState> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceState state = iter.next();
+ if (state.mExpireAt < time) {
+ iter.remove();
+ }
+ }
+ }
+
+ private void processLocation(Location location) {
+ List<PendingIntent> enterIntents = new LinkedList<PendingIntent>();
+ List<PendingIntent> exitIntents = new LinkedList<PendingIntent>();
+
+ synchronized (mLock) {
+ mLastLocation = location;
+
+ removeExpiredFencesLocked();
+
+ for (GeofenceState state : mFences) {
+ if (mBlacklist.isBlacklisted(state.mPackageName)) {
+ if (D) Log.d(TAG, "skipping geofence processing for blacklisted app: " +
+ state.mPackageName);
+ continue;
+ }
+
+ int event = state.processLocation(location);
+ if ((event & GeofenceState.FLAG_ENTER) != 0) {
+ enterIntents.add(state.mIntent);
+ }
+ if ((event & GeofenceState.FLAG_EXIT) != 0) {
+ exitIntents.add(state.mIntent);
+ }
+ }
+ updateProviderRequirementsLocked();
+ }
+
+ // release lock before sending intents
+ for (PendingIntent intent : exitIntents) {
+ sendIntentExit(intent);
+ }
+ for (PendingIntent intent : enterIntents) {
+ sendIntentEnter(intent);
+ }
+ }
+
+ private void sendIntentEnter(PendingIntent pendingIntent) {
+ Intent intent = new Intent();
+ intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
+ sendIntent(pendingIntent, intent);
+ }
+
+ private void sendIntentExit(PendingIntent pendingIntent) {
+ Intent intent = new Intent();
+ intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
+ sendIntent(pendingIntent, intent);
+ }
+
+ private void sendIntent(PendingIntent pendingIntent, Intent intent) {
+ try {
+ mWakeLock.acquire();
+ pendingIntent.send(mContext, 0, intent, this, null, permission.ACCESS_FINE_LOCATION);
+ } catch (PendingIntent.CanceledException e) {
+ removeFence(null, pendingIntent);
+ mWakeLock.release();
+ }
+ }
+
+ private void updateProviderRequirementsLocked() {
+ double minDistance = Double.MAX_VALUE;
+ for (GeofenceState state : mFences) {
+ if (state.getDistance() < minDistance) {
+ minDistance = state.getDistance();
+ }
+ }
+
+ if (minDistance == Double.MAX_VALUE) {
+ disableLocationLocked();
+ } else {
+ int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S;
+ requestLocationLocked(intervalMs);
+ }
+ }
+
+ private void requestLocationLocked(int intervalMs) {
+ mLocationManager.requestLocationUpdates(new LocationRequest().setInterval(intervalMs), this,
+ mLooper);
+ }
+
+ private void disableLocationLocked() {
+ mLocationManager.removeUpdates(this);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ processLocation(location);
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) { }
+
+ @Override
+ public void onProviderEnabled(String provider) { }
+
+ @Override
+ public void onProviderDisabled(String provider) { }
+
+ @Override
+ public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
+ String resultData, Bundle resultExtras) {
+ mWakeLock.release();
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(" Geofences:");
+
+ for (GeofenceState state : mFences) {
+ pw.append(" ");
+ pw.append(state.mPackageName);
+ pw.append(" ");
+ pw.append(state.mFence.toString());
+ pw.append("\n");
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/GeofenceState.java b/services/java/com/android/server/location/GeofenceState.java
new file mode 100644
index 0000000..1fd737f
--- /dev/null
+++ b/services/java/com/android/server/location/GeofenceState.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.server.location;
+
+import android.app.PendingIntent;
+import android.location.Geofence;
+import android.location.Location;
+
+/**
+ * Represents state associated with a geofence
+ */
+public class GeofenceState {
+ public final static int FLAG_ENTER = 0x01;
+ public final static int FLAG_EXIT = 0x02;
+
+ private static final int STATE_UNKNOWN = 0;
+ private static final int STATE_INSIDE = 1;
+ private static final int STATE_OUTSIDE = 2;
+
+ public final Geofence mFence;
+ private final Location mLocation;
+ public final long mExpireAt;
+ public final String mPackageName;
+ public final PendingIntent mIntent;
+
+ int mState; // current state
+ double mDistance; // current distance to center of fence
+
+ public GeofenceState(Geofence fence, Location prevLocation, long expireAt,
+ String packageName, PendingIntent intent) {
+ mState = STATE_UNKNOWN;
+
+ mFence = fence;
+ mExpireAt = expireAt;
+ mPackageName = packageName;
+ mIntent = intent;
+
+ mLocation = new Location("");
+ mLocation.setLatitude(fence.getLatitude());
+ mLocation.setLongitude(fence.getLongitude());
+
+ if (prevLocation != null) {
+ processLocation(prevLocation);
+ }
+ }
+
+ /**
+ * Process a new location.
+ * @return FLAG_ENTER or FLAG_EXIT if the fence was crossed, 0 otherwise
+ */
+ public int processLocation(Location location) {
+ mDistance = mLocation.distanceTo(location);
+
+ int prevState = mState;
+ //TODO: inside/outside detection could be made more rigorous
+ boolean inside = mDistance <= Math.max(mFence.getRadius(), location.getAccuracy());
+ if (inside) {
+ mState = STATE_INSIDE;
+ } else {
+ mState = STATE_OUTSIDE;
+ }
+
+ if (prevState != 0 && mState != prevState) {
+ if (mState == STATE_INSIDE) return FLAG_ENTER;
+ if (mState == STATE_OUTSIDE) return FLAG_EXIT;
+ }
+ return 0;
+ }
+
+ public double getDistance() {
+ return mDistance;
+ }
+
+ @Override
+ public String toString() {
+ String state;
+ switch (mState) {
+ case STATE_INSIDE:
+ state = "IN";
+ break;
+ case STATE_OUTSIDE:
+ state = "OUT";
+ break;
+ default:
+ state = "?";
+ }
+ return String.format("%s d=%.0f %s", mFence.toString(), mDistance, state);
+ }
+}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 4ad6140..c2c0a71 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -29,11 +29,13 @@ import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
+import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -54,17 +56,19 @@ import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.util.NtpTrustedTime;
-import android.util.SparseIntArray;
-
import com.android.internal.app.IBatteryStats;
import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Date;
@@ -81,8 +85,12 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final String TAG = "GpsLocationProvider";
- private static final boolean DEBUG = false;
- private static final boolean VERBOSE = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final ProviderProperties PROPERTIES = new ProviderProperties(
+ true, true, false, false, true, true, true,
+ Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
// these need to match GpsPositionMode enum in gps.h
private static final int GPS_POSITION_MODE_STANDALONE = 0;
@@ -150,14 +158,15 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Handler messages
private static final int CHECK_LOCATION = 1;
private static final int ENABLE = 2;
- private static final int ENABLE_TRACKING = 3;
+ private static final int SET_REQUEST = 3;
private static final int UPDATE_NETWORK_STATE = 4;
private static final int INJECT_NTP_TIME = 5;
private static final int DOWNLOAD_XTRA_DATA = 6;
private static final int UPDATE_LOCATION = 7;
private static final int ADD_LISTENER = 8;
private static final int REMOVE_LISTENER = 9;
- private static final int REQUEST_SINGLE_SHOT = 10;
+ private static final int INJECT_NTP_TIME_FINISHED = 10;
+ private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
// Request setid
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -179,6 +188,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final String PROPERTIES_FILE = "/etc/gps.conf";
+ /** simpler wrapper for ProviderRequest + Worksource */
+ private static class GpsRequest {
+ public ProviderRequest request;
+ public WorkSource source;
+ public GpsRequest(ProviderRequest request, WorkSource source) {
+ this.request = request;
+ this.source = source;
+ }
+ }
+
+ private Object mLock = new Object();
+
private int mLocationFlags = LOCATION_INVALID;
// current status
@@ -198,16 +219,28 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Typical hot TTTF is ~5 seconds, so 10 seconds seems sane.
private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
- // true if we are enabled
- private volatile boolean mEnabled;
-
+ // how often to request NTP time, in milliseconds
+ // current setting 24 hours
+ private static final long NTP_INTERVAL = 24*60*60*1000;
+ // how long to wait if we have a network error in NTP or XTRA downloading
+ // current setting - 5 minutes
+ private static final long RETRY_INTERVAL = 5*60*1000;
+
+ // true if we are enabled, protected by this
+ private boolean mEnabled;
+
// true if we have network connectivity
private boolean mNetworkAvailable;
+ // states for injecting ntp and downloading xtra data
+ private static final int STATE_PENDING_NETWORK = 0;
+ private static final int STATE_DOWNLOADING = 1;
+ private static final int STATE_IDLE = 2;
+
// flags to trigger NTP or XTRA data download when network becomes available
// initialized to true so we do NTP and XTRA when the network comes up after booting
- private boolean mInjectNtpTimePending = true;
- private boolean mDownloadXtraDataPending = true;
+ private int mInjectNtpTimePending = STATE_PENDING_NETWORK;
+ private int mDownloadXtraDataPending = STATE_PENDING_NETWORK;
// set to true if the GPS engine does not do on-demand NTP time requests
private boolean mPeriodicTimeInjection;
@@ -217,16 +250,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
// true if GPS engine is on
private boolean mEngineOn;
-
+
// requested frequency of fixes, in milliseconds
private int mFixInterval = 1000;
// true if we started navigation
private boolean mStarted;
- // true if single shot request is in progress
- private boolean mSingleShot;
-
// capabilities of the GPS engine
private int mEngineCapabilities;
@@ -236,7 +266,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// for calculating time to first fix
private long mFixRequestTime = 0;
// time to first fix for most recent session
- private int mTTFF = 0;
+ private int mTimeToFirstFix = 0;
// time we received our last fix
private long mLastFixTime;
@@ -251,7 +281,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final Context mContext;
private final NtpTrustedTime mNtpTime;
- private final ILocationManager mLocationManager;
+ private final ILocationManager mILocationManager;
private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle();
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
@@ -267,17 +297,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
private int mAGpsDataConnectionState;
private int mAGpsDataConnectionIpAddr;
private final ConnectivityManager mConnMgr;
- private final GpsNetInitiatedHandler mNIHandler;
+ private final GpsNetInitiatedHandler mNIHandler;
// Wakelocks
private final static String WAKELOCK_KEY = "GpsLocationProvider";
private final PowerManager.WakeLock mWakeLock;
- // bitfield of pending messages to our Handler
- // used only for messages that cannot have multiple instances queued
- private int mPendingMessageBits;
- // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
- // which might have multiple instances queued
- private int mPendingListenerMessages;
// Alarms
private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
@@ -287,22 +311,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final PendingIntent mTimeoutIntent;
private final IBatteryStats mBatteryStats;
- private final SparseIntArray mClientUids = new SparseIntArray();
- // how often to request NTP time, in milliseconds
- // current setting 24 hours
- private static final long NTP_INTERVAL = 24*60*60*1000;
- // how long to wait if we have a network error in NTP or XTRA downloading
- // current setting - 5 minutes
- private static final long RETRY_INTERVAL = 5*60*1000;
+ // only modified on handler thread
+ private int[] mClientUids = new int[0];
private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
+ @Override
public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
if (listener == null) {
throw new NullPointerException("listener is null in addGpsStatusListener");
}
- synchronized(mListeners) {
+ synchronized (mListeners) {
IBinder binder = listener.asBinder();
int size = mListeners.size();
for (int i = 0; i < size; i++) {
@@ -319,12 +339,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ @Override
public void removeGpsStatusListener(IGpsStatusListener listener) {
if (listener == null) {
throw new NullPointerException("listener is null in addGpsStatusListener");
}
- synchronized(mListeners) {
+ synchronized (mListeners) {
IBinder binder = listener.asBinder();
Listener l = null;
int size = mListeners.size();
@@ -353,7 +374,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (action.equals(ALARM_WAKEUP)) {
if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
- startNavigating(false);
+ startNavigating();
} else if (action.equals(ALARM_TIMEOUT)) {
if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
@@ -361,6 +382,22 @@ public class GpsLocationProvider implements LocationProviderInterface {
checkSmsSuplInit(intent);
} else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
checkWapSuplInit(intent);
+ } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ int networkState;
+ if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
+ networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
+ } else {
+ networkState = LocationProvider.AVAILABLE;
+ }
+
+ // retrieve NetworkInfo result for this UID
+ NetworkInfo info =
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+ ConnectivityManager connManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ info = connManager.getNetworkInfo(info.getType());
+
+ updateNetworkState(networkState, info);
}
}
};
@@ -382,10 +419,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
return native_is_supported();
}
- public GpsLocationProvider(Context context, ILocationManager locationManager) {
+ public GpsLocationProvider(Context context, ILocationManager ilocationManager) {
mContext = context;
mNtpTime = NtpTrustedTime.getInstance(context);
- mLocationManager = locationManager;
+ mILocationManager = ilocationManager;
mNIHandler = new GpsNetInitiatedHandler(context);
mLocation.setExtras(mLocationExtras);
@@ -393,7 +430,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Create a wake lock
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- mWakeLock.setReferenceCounted(false);
+ mWakeLock.setReferenceCounted(true);
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
@@ -467,22 +504,21 @@ public class GpsLocationProvider implements LocationProviderInterface {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(mBroadcastReciever, intentFilter);
}
/**
* Returns the name of this provider.
*/
+ @Override
public String getName() {
return LocationManager.GPS_PROVIDER;
}
- /**
- * Returns true if the provider requires access to a
- * data network (e.g., the Internet), false otherwise.
- */
- public boolean requiresNetwork() {
- return true;
+ @Override
+ public ProviderProperties getProperties() {
+ return PROPERTIES;
}
public void updateNetworkState(int state, NetworkInfo info) {
@@ -516,7 +552,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
String apnName = info.getExtraInfo();
if (mNetworkAvailable) {
if (apnName == null) {
- /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
+ /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
exception in the following call to native_agps_data_conn_open*/
apnName = "dummy-apn";
}
@@ -541,88 +577,111 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
if (mNetworkAvailable) {
- if (mInjectNtpTimePending) {
+ if (mInjectNtpTimePending == STATE_PENDING_NETWORK) {
sendMessage(INJECT_NTP_TIME, 0, null);
}
- if (mDownloadXtraDataPending) {
+ if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
}
}
}
private void handleInjectNtpTime() {
+ if (mInjectNtpTimePending == STATE_DOWNLOADING) {
+ // already downloading data
+ return;
+ }
if (!mNetworkAvailable) {
// try again when network is up
- mInjectNtpTimePending = true;
+ mInjectNtpTimePending = STATE_PENDING_NETWORK;
return;
}
- mInjectNtpTimePending = false;
+ mInjectNtpTimePending = STATE_DOWNLOADING;
- long delay;
+ // hold wake lock while task runs
+ mWakeLock.acquire();
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+ @Override
+ public void run() {
+ long delay;
- // force refresh NTP cache when outdated
- if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
- mNtpTime.forceRefresh();
- }
+ // force refresh NTP cache when outdated
+ if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
+ mNtpTime.forceRefresh();
+ }
- // only update when NTP time is fresh
- if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
- long time = mNtpTime.getCachedNtpTime();
- long timeReference = mNtpTime.getCachedNtpTimeReference();
- long certainty = mNtpTime.getCacheCertainty();
- long now = System.currentTimeMillis();
-
- Log.d(TAG, "NTP server returned: "
- + time + " (" + new Date(time)
- + ") reference: " + timeReference
- + " certainty: " + certainty
- + " system time offset: " + (time - now));
-
- native_inject_time(time, timeReference, (int) certainty);
- delay = NTP_INTERVAL;
- } else {
- if (DEBUG) Log.d(TAG, "requestTime failed");
- delay = RETRY_INTERVAL;
- }
+ // only update when NTP time is fresh
+ if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
+ long time = mNtpTime.getCachedNtpTime();
+ long timeReference = mNtpTime.getCachedNtpTimeReference();
+ long certainty = mNtpTime.getCacheCertainty();
+ long now = System.currentTimeMillis();
+
+ Log.d(TAG, "NTP server returned: "
+ + time + " (" + new Date(time)
+ + ") reference: " + timeReference
+ + " certainty: " + certainty
+ + " system time offset: " + (time - now));
+
+ native_inject_time(time, timeReference, (int) certainty);
+ delay = NTP_INTERVAL;
+ } else {
+ if (DEBUG) Log.d(TAG, "requestTime failed");
+ delay = RETRY_INTERVAL;
+ }
- if (mPeriodicTimeInjection) {
- // send delayed message for next NTP injection
- // since this is delayed and not urgent we do not hold a wake lock here
- mHandler.removeMessages(INJECT_NTP_TIME);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
- }
+ sendMessage(INJECT_NTP_TIME_FINISHED, 0, null);
+
+ if (mPeriodicTimeInjection) {
+ // send delayed message for next NTP injection
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.sendEmptyMessageDelayed(INJECT_NTP_TIME, delay);
+ }
+
+ // release wake lock held by task
+ mWakeLock.release();
+ }
+ });
}
private void handleDownloadXtraData() {
+ if (mDownloadXtraDataPending == STATE_DOWNLOADING) {
+ // already downloading data
+ return;
+ }
if (!mNetworkAvailable) {
// try again when network is up
- mDownloadXtraDataPending = true;
+ mDownloadXtraDataPending = STATE_PENDING_NETWORK;
return;
}
- mDownloadXtraDataPending = false;
+ mDownloadXtraDataPending = STATE_DOWNLOADING;
+
+ // hold wake lock while task runs
+ mWakeLock.acquire();
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+ @Override
+ public void run() {
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) {
+ Log.d(TAG, "calling native_inject_xtra_data");
+ }
+ native_inject_xtra_data(data, data.length);
+ }
+ sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
- GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
- byte[] data = xtraDownloader.downloadXtraData();
- if (data != null) {
- if (DEBUG) {
- Log.d(TAG, "calling native_inject_xtra_data");
- }
- native_inject_xtra_data(data, data.length);
- } else {
- // try again later
- // since this is delayed and not urgent we do not hold a wake lock here
- mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
- }
- }
+ if (data == null) {
+ // try again later
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA, RETRY_INTERVAL);
+ }
- /**
- * This is called to inform us when another location provider returns a location.
- * Someday we might use this for network location injection to aid the GPS
- */
- public void updateLocation(Location location) {
- sendMessage(UPDATE_LOCATION, 0, location);
+ // release wake lock held by task
+ mWakeLock.release();
+ }
+ });
}
private void handleUpdateLocation(Location location) {
@@ -633,107 +692,26 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
/**
- * Returns true if the provider requires access to a
- * satellite-based positioning system (e.g., GPS), false
- * otherwise.
- */
- public boolean requiresSatellite() {
- return true;
- }
-
- /**
- * Returns true if the provider requires access to an appropriate
- * cellular network (e.g., to make use of cell tower IDs), false
- * otherwise.
- */
- public boolean requiresCell() {
- return false;
- }
-
- /**
- * Returns true if the use of this provider may result in a
- * monetary charge to the user, false if use is free. It is up to
- * each provider to give accurate information.
- */
- public boolean hasMonetaryCost() {
- return false;
- }
-
- /**
- * Returns true if the provider is able to provide altitude
- * information, false otherwise. A provider that reports altitude
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsAltitude() {
- return true;
- }
-
- /**
- * Returns true if the provider is able to provide speed
- * information, false otherwise. A provider that reports speed
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsSpeed() {
- return true;
- }
-
- /**
- * Returns true if the provider is able to provide bearing
- * information, false otherwise. A provider that reports bearing
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsBearing() {
- return true;
- }
-
- /**
- * Returns the power requirement for this provider.
- *
- * @return the power requirement for this provider, as one of the
- * constants Criteria.POWER_REQUIREMENT_*.
- */
- public int getPowerRequirement() {
- return Criteria.POWER_HIGH;
- }
-
- /**
- * Returns true if this provider meets the given criteria,
- * false otherwise.
- */
- public boolean meetsCriteria(Criteria criteria) {
- return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
- }
-
- /**
- * Returns the horizontal accuracy of this provider
- *
- * @return the accuracy of location from this provider, as one
- * of the constants Criteria.ACCURACY_*.
- */
- public int getAccuracy() {
- return Criteria.ACCURACY_FINE;
- }
-
- /**
* Enables this provider. When enabled, calls to getStatus()
* must be handled. Hardware may be started up
* when the provider is enabled.
*/
+ @Override
public void enable() {
- synchronized (mHandler) {
- sendMessage(ENABLE, 1, null);
- }
+ sendMessage(ENABLE, 1, null);
}
private void handleEnable() {
if (DEBUG) Log.d(TAG, "handleEnable");
- if (mEnabled) return;
- mEnabled = native_init();
- if (mEnabled) {
+ synchronized (mLock) {
+ if (mEnabled) return;
+ mEnabled = true;
+ }
+
+ boolean enabled = native_init();
+
+ if (enabled) {
mSupportsXtra = native_supports_xtra();
if (mSuplServerHost != null) {
native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
@@ -742,6 +720,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
}
} else {
+ synchronized (mLock) {
+ mEnabled = false;
+ }
Log.w(TAG, "Failed to enable location provider");
}
}
@@ -751,27 +732,35 @@ public class GpsLocationProvider implements LocationProviderInterface {
* need not be handled. Hardware may be shut
* down while the provider is disabled.
*/
+ @Override
public void disable() {
- synchronized (mHandler) {
- sendMessage(ENABLE, 0, null);
- }
+ sendMessage(ENABLE, 0, null);
}
private void handleDisable() {
if (DEBUG) Log.d(TAG, "handleDisable");
- if (!mEnabled) return;
- mEnabled = false;
+ synchronized (mLock) {
+ if (!mEnabled) return;
+ mEnabled = false;
+ }
+
stopNavigating();
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
// do this before releasing wakelock
native_cleanup();
}
+ @Override
public boolean isEnabled() {
- return mEnabled;
+ synchronized (mLock) {
+ return mEnabled;
+ }
}
+ @Override
public int getStatus(Bundle extras) {
if (extras != null) {
extras.putInt("satellites", mSvCount);
@@ -788,93 +777,69 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ @Override
public long getStatusUpdateTime() {
return mStatusUpdateTime;
}
- public void enableLocationTracking(boolean enable) {
- // FIXME - should set a flag here to avoid race conditions with single shot request
- synchronized (mHandler) {
- sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
- }
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
}
- private void handleEnableLocationTracking(boolean enable) {
- if (enable) {
- mTTFF = 0;
- mLastFixTime = 0;
- startNavigating(false);
- } else {
- if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
- mAlarmManager.cancel(mWakeupIntent);
- mAlarmManager.cancel(mTimeoutIntent);
- }
- stopNavigating();
- }
- }
+ private void handleSetRequest(ProviderRequest request, WorkSource source) {
+ if (DEBUG) Log.d(TAG, "setRequest " + request);
- public boolean requestSingleShotFix() {
- if (mStarted) {
- // cannot do single shot if already navigating
- return false;
- }
- synchronized (mHandler) {
- mHandler.removeMessages(REQUEST_SINGLE_SHOT);
- Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
- mHandler.sendMessage(m);
- }
- return true;
- }
- private void handleRequestSingleShot() {
- mTTFF = 0;
- mLastFixTime = 0;
- startNavigating(true);
- }
- public void setMinTime(long minTime, WorkSource ws) {
- if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
-
- if (minTime >= 0) {
- mFixInterval = (int)minTime;
+ if (request.reportLocation) {
+ // update client uids
+ int[] uids = new int[source.size()];
+ for (int i=0; i < source.size(); i++) {
+ uids[i] = source.get(i);
+ }
+ updateClientUids(uids);
+
+ mFixInterval = (int) request.interval;
+ // check for overflow
+ if (mFixInterval != request.interval) {
+ Log.w(TAG, "interval overflow: " + request.interval);
+ mFixInterval = Integer.MAX_VALUE;
+ }
+
+ // apply request to GPS engine
if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ // change period
if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
mFixInterval, 0, 0)) {
Log.e(TAG, "set_position_mode failed in setMinTime()");
}
+ } else if (!mStarted) {
+ // start GPS
+ startNavigating();
}
- }
- }
-
- public String getInternalState() {
- StringBuilder s = new StringBuilder();
- s.append(" mFixInterval=").append(mFixInterval).append("\n");
- s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
- if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
- if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
- if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
- if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
- s.append(")\n");
+ } else {
+ updateClientUids(new int[0]);
- s.append(native_get_internal_state());
- return s.toString();
+ stopNavigating();
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
}
private final class Listener implements IBinder.DeathRecipient {
final IGpsStatusListener mListener;
-
- int mSensors = 0;
-
+
Listener(IGpsStatusListener listener) {
mListener = listener;
}
-
+
+ @Override
public void binderDied() {
if (DEBUG) Log.d(TAG, "GPS status listener died");
- synchronized(mListeners) {
+ synchronized (mListeners) {
mListeners.remove(this);
}
if (mListener != null) {
@@ -883,64 +848,50 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- public void addListener(int uid) {
- synchronized (mWakeLock) {
- mPendingListenerMessages++;
- mWakeLock.acquire();
- Message m = Message.obtain(mHandler, ADD_LISTENER);
- m.arg1 = uid;
- mHandler.sendMessage(m);
- }
- }
-
- private void handleAddListener(int uid) {
- synchronized(mListeners) {
- if (mClientUids.indexOfKey(uid) >= 0) {
- // Shouldn't be here -- already have this uid.
- Log.w(TAG, "Duplicate add listener for uid " + uid);
- return;
+ private void updateClientUids(int[] uids) {
+ // Find uid's that were not previously tracked
+ for (int uid1 : uids) {
+ boolean newUid = true;
+ for (int uid2 : mClientUids) {
+ if (uid1 == uid2) {
+ newUid = false;
+ break;
+ }
}
- mClientUids.put(uid, 0);
- if (mNavigating) {
+ if (newUid) {
try {
- mBatteryStats.noteStartGps(uid);
+ mBatteryStats.noteStartGps(uid1);
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException in addListener");
+ Log.w(TAG, "RemoteException", e);
}
}
}
- }
-
- public void removeListener(int uid) {
- synchronized (mWakeLock) {
- mPendingListenerMessages++;
- mWakeLock.acquire();
- Message m = Message.obtain(mHandler, REMOVE_LISTENER);
- m.arg1 = uid;
- mHandler.sendMessage(m);
- }
- }
- private void handleRemoveListener(int uid) {
- synchronized(mListeners) {
- if (mClientUids.indexOfKey(uid) < 0) {
- // Shouldn't be here -- don't have this uid.
- Log.w(TAG, "Unneeded remove listener for uid " + uid);
- return;
+ // Find uid'd that were tracked but have now disappeared
+ for (int uid1 : mClientUids) {
+ boolean oldUid = true;
+ for (int uid2 : uids) {
+ if (uid1 == uid2) {
+ oldUid = false;
+ break;
+ }
}
- mClientUids.delete(uid);
- if (mNavigating) {
+ if (oldUid) {
try {
- mBatteryStats.noteStopGps(uid);
+ mBatteryStats.noteStopGps(uid1);
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException in removeListener");
+ Log.w(TAG, "RemoteException", e);
}
}
}
+
+ // save current uids
+ mClientUids = uids;
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
-
+
long identity = Binder.clearCallingIdentity();
boolean result = false;
@@ -957,7 +908,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
-
+
Binder.restoreCallingIdentity(identity);
return result;
}
@@ -992,18 +943,17 @@ public class GpsLocationProvider implements LocationProviderInterface {
return false;
}
- private void startNavigating(boolean singleShot) {
+ private void startNavigating() {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
+ mTimeToFirstFix = 0;
+ mLastFixTime = 0;
mStarted = true;
- mSingleShot = singleShot;
mPositionMode = GPS_POSITION_MODE_STANDALONE;
if (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
- if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
- mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
- } else if (hasCapability(GPS_CAPABILITY_MSB)) {
+ if (hasCapability(GPS_CAPABILITY_MSB)) {
mPositionMode = GPS_POSITION_MODE_MS_BASED;
}
}
@@ -1039,9 +989,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
mStarted = false;
- mSingleShot = false;
native_stop();
- mTTFF = 0;
+ mTimeToFirstFix = 0;
mLastFixTime = 0;
mLocationFlags = LOCATION_INVALID;
@@ -1056,8 +1005,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
mAlarmManager.cancel(mTimeoutIntent);
mAlarmManager.cancel(mWakeupIntent);
long now = SystemClock.elapsedRealtime();
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent);
}
private boolean hasCapability(int capability) {
@@ -1078,6 +1026,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
mLocation.setLatitude(latitude);
mLocation.setLongitude(longitude);
mLocation.setTime(timestamp);
+ // It would be nice to push the elapsed real-time timestamp
+ // further down the stack, but this is still useful
+ mLocation.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
}
if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
mLocation.setAltitude(altitude);
@@ -1102,7 +1053,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
mLocation.setExtras(mLocationExtras);
try {
- mLocationManager.reportLocation(mLocation, false);
+ mILocationManager.reportLocation(mLocation, false);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling reportLocation");
}
@@ -1110,17 +1061,17 @@ public class GpsLocationProvider implements LocationProviderInterface {
mLastFixTime = System.currentTimeMillis();
// report time to first fix
- if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
- mTTFF = (int)(mLastFixTime - mFixRequestTime);
- if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
+ if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
+ if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
// notify status listeners
- synchronized(mListeners) {
+ synchronized (mListeners) {
int size = mListeners.size();
for (int i = 0; i < size; i++) {
Listener listener = mListeners.get(i);
try {
- listener.mListener.onFirstFix(mTTFF);
+ listener.mListener.onFirstFix(mTimeToFirstFix);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in stopNavigating");
mListeners.remove(listener);
@@ -1131,9 +1082,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- if (mSingleShot) {
- stopNavigating();
- }
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// we want to time out if we do not receive a fix
// within the time out and we are requesting infrequent fixes
@@ -1161,7 +1109,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private void reportStatus(int status) {
if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
- synchronized(mListeners) {
+ synchronized (mListeners) {
boolean wasNavigating = mNavigating;
switch (status) {
@@ -1199,20 +1147,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- try {
- // update battery stats
- for (int i=mClientUids.size() - 1; i >= 0; i--) {
- int uid = mClientUids.keyAt(i);
- if (mNavigating) {
- mBatteryStats.noteStartGps(uid);
- } else {
- mBatteryStats.noteStopGps(uid);
- }
- }
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException in reportStatus");
- }
-
// send an intent to notify that the GPS has been enabled or disabled.
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
@@ -1227,15 +1161,15 @@ public class GpsLocationProvider implements LocationProviderInterface {
private void reportSvStatus() {
int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
-
- synchronized(mListeners) {
+
+ synchronized (mListeners) {
int size = mListeners.size();
for (int i = 0; i < size; i++) {
Listener listener = mListeners.get(i);
try {
- listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
- mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
- mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
+ listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
+ mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
+ mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in reportSvInfo");
mListeners.remove(listener);
@@ -1251,7 +1185,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
" almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
for (int i = 0; i < svCount; i++) {
Log.v(TAG, "sv: " + mSvs[i] +
- " snr: " + (float)mSnrs[i]/10 +
+ " snr: " + mSnrs[i]/10 +
" elev: " + mSvElevations[i] +
" azimuth: " + mSvAzimuths[i] +
((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") +
@@ -1339,7 +1273,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
* called from native code to report NMEA data received
*/
private void reportNmea(long timestamp) {
- synchronized(mListeners) {
+ synchronized (mListeners) {
int size = mListeners.size();
if (size > 0) {
// don't bother creating the String if we have no listeners
@@ -1386,19 +1320,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
//=============================================================
private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
// Sends a response for an NI reqeust to HAL.
+ @Override
public boolean sendNiResponse(int notificationId, int userResponse)
{
// TODO Add Permission check
- StringBuilder extrasBuf = new StringBuilder();
-
if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
", response: " + userResponse);
native_send_ni_response(notificationId, userResponse);
return true;
}
};
-
+
public INetInitiatedListener getNetInitiatedListener() {
return mNetInitiatedListener;
}
@@ -1547,19 +1480,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
private void sendMessage(int message, int arg, Object obj) {
- // hold a wake lock while messages are pending
- synchronized (mWakeLock) {
- mPendingMessageBits |= (1 << message);
- mWakeLock.acquire();
- mHandler.removeMessages(message);
- Message m = Message.obtain(mHandler, message);
- m.arg1 = arg;
- m.obj = obj;
- mHandler.sendMessage(m);
- }
+ // hold a wake lock until this message is delivered
+ // note that this assumes the message will not be removed from the queue before
+ // it is handled (otherwise the wake lock would be leaked).
+ mWakeLock.acquire();
+ mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
}
private final class ProviderHandler extends Handler {
+ public ProviderHandler() {
+ super(true /*async*/);
+ }
+
@Override
public void handleMessage(Message msg) {
int message = msg.what;
@@ -1571,11 +1503,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
handleDisable();
}
break;
- case ENABLE_TRACKING:
- handleEnableLocationTracking(msg.arg1 == 1);
- break;
- case REQUEST_SINGLE_SHOT:
- handleRequestSingleShot();
+ case SET_REQUEST:
+ GpsRequest gpsRequest = (GpsRequest) msg.obj;
+ handleSetRequest(gpsRequest.request, gpsRequest.source);
break;
case UPDATE_NETWORK_STATE:
handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
@@ -1588,25 +1518,19 @@ public class GpsLocationProvider implements LocationProviderInterface {
handleDownloadXtraData();
}
break;
- case UPDATE_LOCATION:
- handleUpdateLocation((Location)msg.obj);
+ case INJECT_NTP_TIME_FINISHED:
+ mInjectNtpTimePending = STATE_IDLE;
break;
- case ADD_LISTENER:
- handleAddListener(msg.arg1);
+ case DOWNLOAD_XTRA_DATA_FINISHED:
+ mDownloadXtraDataPending = STATE_IDLE;
break;
- case REMOVE_LISTENER:
- handleRemoveListener(msg.arg1);
+ case UPDATE_LOCATION:
+ handleUpdateLocation((Location)msg.obj);
break;
}
- // release wake lock if no messages are pending
- synchronized (mWakeLock) {
- mPendingMessageBits &= ~(1 << message);
- if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
- mPendingListenerMessages--;
- }
- if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
- mWakeLock.release();
- }
+ if (msg.arg2 == 1) {
+ // wakelock was taken for this message, release it
+ mWakeLock.release();
}
}
};
@@ -1617,17 +1541,39 @@ public class GpsLocationProvider implements LocationProviderInterface {
super("GpsLocationProvider");
}
+ @Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
initialize();
Looper.prepare();
+
+ LocationManager locManager =
+ (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
mHandler = new ProviderHandler();
// signal when we are initialized and ready to go
mInitializedLatch.countDown();
+ locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+ 0, 0, new NetworkLocationListener(), Looper.myLooper());
Looper.loop();
}
}
+ private final class NetworkLocationListener implements LocationListener {
+ @Override
+ public void onLocationChanged(Location location) {
+ // this callback happens on mHandler looper
+ if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
+ handleUpdateLocation(location);
+ }
+ }
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) { }
+ @Override
+ public void onProviderEnabled(String provider) { }
+ @Override
+ public void onProviderDisabled(String provider) { }
+ }
+
private String getSelectedApn() {
Uri uri = Uri.parse("content://telephony/carriers/preferapn");
String apn = null;
@@ -1647,6 +1593,22 @@ public class GpsLocationProvider implements LocationProviderInterface {
return apn;
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ StringBuilder s = new StringBuilder();
+ s.append(" mFixInterval=").append(mFixInterval).append("\n");
+ s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
+ if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
+ if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
+ if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
+ if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
+ s.append(")\n");
+
+ s.append(native_get_internal_state());
+ pw.append(s);
+ }
+
// for GPS SV statistics
private static final int MAX_SVS = 32;
private static final int EPHEMERIS_MASK = 0;
diff --git a/services/java/com/android/server/location/LocationBasedCountryDetector.java b/services/java/com/android/server/location/LocationBasedCountryDetector.java
index d4fb8ee..38871d7 100755
--- a/services/java/com/android/server/location/LocationBasedCountryDetector.java
+++ b/services/java/com/android/server/location/LocationBasedCountryDetector.java
@@ -114,7 +114,9 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
for (String provider : providers) {
Location lastKnownLocation = mLocationManager.getLastKnownLocation(provider);
if (lastKnownLocation != null) {
- if (bestLocation == null || bestLocation.getTime() < lastKnownLocation.getTime()) {
+ if (bestLocation == null ||
+ bestLocation.getElapsedRealtimeNano() <
+ lastKnownLocation.getElapsedRealtimeNano()) {
bestLocation = lastKnownLocation;
}
}
diff --git a/services/java/com/android/server/location/LocationBlacklist.java b/services/java/com/android/server/location/LocationBlacklist.java
new file mode 100644
index 0000000..71fa9f9
--- /dev/null
+++ b/services/java/com/android/server/location/LocationBlacklist.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.LocationManagerService;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Allows applications to be blacklisted from location updates at run-time.
+ *
+ * This is a silent blacklist. Applications can still call Location Manager
+ * API's, but they just won't receive any locations.
+ */
+public final class LocationBlacklist extends ContentObserver {
+ private static final String TAG = "LocationBlacklist";
+ private static final boolean D = LocationManagerService.D;
+ private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist";
+ private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist";
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+
+ // all fields below synchronized on mLock
+ private String[] mWhitelist = new String[0];
+ private String[] mBlacklist = new String[0];
+
+ public LocationBlacklist(Context context, Handler handler) {
+ super(handler);
+ mContext = context;
+ }
+
+ public void init() {
+ mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ BLACKLIST_CONFIG_NAME), false, this);
+// mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+// WHITELIST_CONFIG_NAME), false, this);
+ reloadBlacklist();
+ }
+
+ private void reloadBlacklist() {
+ String blacklist[] = getStringArray(BLACKLIST_CONFIG_NAME);
+ String whitelist[] = getStringArray(WHITELIST_CONFIG_NAME);
+ synchronized (mLock) {
+ mWhitelist = whitelist;
+ Slog.i(TAG, "whitelist: " + Arrays.toString(mWhitelist));
+ mBlacklist = blacklist;
+ Slog.i(TAG, "blacklist: " + Arrays.toString(mBlacklist));
+ }
+ }
+
+ /**
+ * Return true if in blacklist
+ * (package name matches blacklist, and does not match whitelist)
+ */
+ public boolean isBlacklisted(String packageName) {
+ synchronized (mLock) {
+ for (String black : mBlacklist) {
+ if (packageName.startsWith(black)) {
+ if (inWhitelist(packageName)) {
+ continue;
+ } else {
+ if (D) Log.d(TAG, "dropping location (blacklisted): "
+ + packageName + " matches " + black);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return true if any of packages are in whitelist
+ */
+ private boolean inWhitelist(String pkg) {
+ synchronized (mLock) {
+ for (String white : mWhitelist) {
+ if (pkg.startsWith(white)) return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ reloadBlacklist();
+ }
+
+ private String[] getStringArray(String key) {
+ String flatString = Settings.Secure.getString(mContext.getContentResolver(), key);
+ if (flatString == null) {
+ return new String[0];
+ }
+ String[] splitStrings = flatString.split(",");
+ ArrayList<String> result = new ArrayList<String>();
+ for (String pkg : splitStrings) {
+ pkg = pkg.trim();
+ if (pkg.isEmpty()) {
+ continue;
+ }
+ result.add(pkg);
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("mWhitelist=" + Arrays.toString(mWhitelist) + " mBlacklist=" +
+ Arrays.toString(mBlacklist));
+ }
+}
diff --git a/services/java/com/android/server/location/LocationFudger.java b/services/java/com/android/server/location/LocationFudger.java
new file mode 100644
index 0000000..57bc1c5
--- /dev/null
+++ b/services/java/com/android/server/location/LocationFudger.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.SecureRandom;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
+
+
+/**
+ * Contains the logic to obfuscate (fudge) locations for coarse applications.
+ *
+ * <p>The goal is just to prevent applications with only
+ * the coarse location permission from receiving a fine location.
+ */
+public class LocationFudger {
+ private static final boolean D = false;
+ private static final String TAG = "LocationFudge";
+
+ private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
+
+ /**
+ * This is the main control: Best location accuracy allowed for coarse applications.
+ */
+ private static final float ACCURACY_METERS = 200.0f;
+
+ /**
+ * The distance between grids for snap-to-grid. See {@link #createCoarse}.
+ */
+ private static final double GRID_SIZE_METERS = ACCURACY_METERS;
+
+ /**
+ * Standard deviation of the (normally distributed) random offset applied
+ * to coarse locations. It does not need to be as large as
+ * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
+ * method. See further details in the implementation.
+ */
+ private static final double STANDARD_DEVIATION_METERS = GRID_SIZE_METERS / 4.0;
+
+ /**
+ * This is the fastest interval that applications can receive coarse
+ * locations.
+ */
+ public static final long FASTEST_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
+
+ /**
+ * The duration until we change the random offset.
+ */
+ private static final long CHANGE_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
+
+ /**
+ * The percentage that we change the random offset at every interval.
+ *
+ * <p>0.0 indicates the random offset doesn't change. 1.0
+ * indicates the random offset is completely replaced every interval.
+ */
+ private static final double CHANGE_PER_INTERVAL = 0.03; // 3% change
+
+ // Pre-calculated weights used to move the random offset.
+ //
+ // The goal is to iterate on the previous offset, but keep
+ // the resulting standard deviation the same. The variance of
+ // two gaussian distributions summed together is equal to the
+ // sum of the variance of each distribution. So some quick
+ // algebra results in the following sqrt calculation to
+ // weigh in a new offset while keeping the final standard
+ // deviation unchanged.
+ private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL;
+ private static final double PREVIOUS_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT);
+
+ /**
+ * This number actually varies because the earth is not round, but
+ * 111,000 meters is considered generally acceptable.
+ */
+ private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
+
+ /**
+ * Maximum latitude.
+ *
+ * <p>We pick a value 1 meter away from 90.0 degrees in order
+ * to keep cosine(MAX_LATITUDE) to a non-zero value, so that we avoid
+ * divide by zero fails.
+ */
+ private static final double MAX_LATITUDE = 90.0 -
+ (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
+
+ private final Object mLock = new Object();
+ private final SecureRandom mRandom = new SecureRandom();
+
+ // all fields below protected by mLock
+ private double mOffsetLatitudeMeters;
+ private double mOffsetLongitudeMeters;
+ private long mNextInterval;
+
+ public LocationFudger() {
+ mOffsetLatitudeMeters = nextOffset();
+ mOffsetLongitudeMeters = nextOffset();
+ mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS;
+ }
+
+ /**
+ * Get the cached coarse location, or generate a new one and cache it.
+ */
+ public Location getOrCreate(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) {
+ return addCoarseLocationExtra(location);
+ }
+ Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
+ if (parcel == null) {
+ return addCoarseLocationExtra(location);
+ }
+ if (!(parcel instanceof Location)) {
+ return addCoarseLocationExtra(location);
+ }
+ Location coarse = (Location) parcel;
+ if (coarse.getAccuracy() < ACCURACY_METERS) {
+ return addCoarseLocationExtra(location);
+ }
+ return coarse;
+ }
+
+ private Location addCoarseLocationExtra(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) extras = new Bundle();
+ Location coarse = createCoarse(location);
+ extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
+ location.setExtras(extras);
+ return coarse;
+ }
+
+ /**
+ * Create a coarse location.
+ *
+ * <p>Two techniques are used: random offsets and snap-to-grid.
+ *
+ * <p>First we add a random offset. This mitigates against detecting
+ * grid transitions. Without a random offset it is possible to detect
+ * a users position very accurately when they cross a grid boundary.
+ * The random offset changes very slowly over time, to mitigate against
+ * taking many location samples and averaging them out.
+ *
+ * <p>Second we snap-to-grid (quantize). This has the nice property of
+ * producing stable results, and mitigating against taking many samples
+ * to average out a random offset.
+ */
+ private Location createCoarse(Location fine) {
+ Location coarse = new Location(fine);
+
+ // clean all the optional information off the location, because
+ // this can leak detailed location information
+ coarse.removeBearing();
+ coarse.removeSpeed();
+ coarse.removeAltitude();
+ coarse.setExtras(null);
+
+ double lat = coarse.getLatitude();
+ double lon = coarse.getLongitude();
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // Step 1) apply a random offset
+ //
+ // The goal of the random offset is to prevent the application
+ // from determining that the device is on a grid boundary
+ // when it crosses from one grid to the next.
+ //
+ // We apply the offset even if the location already claims to be
+ // inaccurate, because it may be more accurate than claimed.
+ synchronized (mLock) {
+ updateRandomOffsetLocked();
+ // perform lon first whilst lat is still within bounds
+ lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat);
+ lat += metersToDegreesLatitude(mOffsetLatitudeMeters);
+ if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)",
+ mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+ }
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // Step 2) Snap-to-grid (quantize)
+ //
+ // This is the primary means of obfuscation. It gives nice consistent
+ // results and is very effective at hiding the true location
+ // (as long as you are not sitting on a grid boundary, which
+ // step 1 mitigates).
+ //
+ // Note we quantize the latitude first, since the longitude
+ // quantization depends on the latitude value and so leaks information
+ // about the latitude
+ double latGranularity = metersToDegreesLatitude(GRID_SIZE_METERS);
+ lat = Math.round(lat / latGranularity) * latGranularity;
+ double lonGranularity = metersToDegreesLongitude(GRID_SIZE_METERS, lat);
+ lon = Math.round(lon / lonGranularity) * lonGranularity;
+
+ // wrap again
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // apply
+ coarse.setLatitude(lat);
+ coarse.setLongitude(lon);
+ coarse.setAccuracy(Math.max(ACCURACY_METERS, coarse.getAccuracy()));
+
+ if (D) Log.d(TAG, "fudged " + fine + " to " + coarse);
+ return coarse;
+ }
+
+ /**
+ * Update the random offset over time.
+ *
+ * <p>If the random offset was new for every location
+ * fix then an application can more easily average location results
+ * over time,
+ * especially when the location is near a grid boundary. On the
+ * other hand if the random offset is constant then if an application
+ * found a way to reverse engineer the offset they would be able
+ * to detect location at grid boundaries very accurately. So
+ * we choose a random offset and then very slowly move it, to
+ * make both approaches very hard.
+ *
+ * <p>The random offset does not need to be large, because snap-to-grid
+ * is the primary obfuscation mechanism. It just needs to be large
+ * enough to stop information leakage as we cross grid boundaries.
+ */
+ private void updateRandomOffsetLocked() {
+ long now = SystemClock.elapsedRealtime();
+ if (now < mNextInterval) {
+ return;
+ }
+
+ if (D) Log.d(TAG, String.format("old offset: %.0f, %.0f (meters)",
+ mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+
+ // ok, need to update the random offset
+ mNextInterval = now + CHANGE_INTERVAL_MS;
+
+ mOffsetLatitudeMeters *= PREVIOUS_WEIGHT;
+ mOffsetLatitudeMeters += NEW_WEIGHT * nextOffset();
+ mOffsetLongitudeMeters *= PREVIOUS_WEIGHT;
+ mOffsetLongitudeMeters += NEW_WEIGHT * nextOffset();
+
+ if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)",
+ mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+ }
+
+ private double nextOffset() {
+ return mRandom.nextGaussian() * STANDARD_DEVIATION_METERS;
+ }
+
+ private static double wrapLatitude(double lat) {
+ if (lat > MAX_LATITUDE) {
+ lat = MAX_LATITUDE;
+ }
+ if (lat < -MAX_LATITUDE) {
+ lat = -MAX_LATITUDE;
+ }
+ return lat;
+ }
+
+ private static double wrapLongitude(double lon) {
+ lon %= 360.0; // wraps into range (-360.0, +360.0)
+ if (lon >= 180.0) {
+ lon -= 360.0;
+ }
+ if (lon < -180.0) {
+ lon += 360.0;
+ }
+ return lon;
+ }
+
+ private static double metersToDegreesLatitude(double distance) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
+ }
+
+ /**
+ * Requires latitude since longitudinal distances change with distance from equator.
+ */
+ private static double metersToDegreesLongitude(double distance, double lat) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat));
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters,
+ mOffsetLatitudeMeters));
+ }
+}
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 858a582..6f09232 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -16,42 +16,33 @@
package com.android.server.location;
-import android.location.Criteria;
-import android.location.Location;
-import android.net.NetworkInfo;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+
import android.os.Bundle;
import android.os.WorkSource;
/**
* Location Manager's interface for location providers.
- *
- * {@hide}
+ * @hide
*/
public interface LocationProviderInterface {
- String getName();
- boolean requiresNetwork();
- boolean requiresSatellite();
- boolean requiresCell();
- boolean hasMonetaryCost();
- boolean supportsAltitude();
- boolean supportsSpeed();
- boolean supportsBearing();
- int getPowerRequirement();
- boolean meetsCriteria(Criteria criteria);
- int getAccuracy();
- boolean isEnabled();
- void enable();
- void disable();
- int getStatus(Bundle extras);
- long getStatusUpdateTime();
- void enableLocationTracking(boolean enable);
- /* returns false if single shot is not supported */
- boolean requestSingleShotFix();
- String getInternalState();
- void setMinTime(long minTime, WorkSource ws);
- void updateNetworkState(int state, NetworkInfo info);
- void updateLocation(Location location);
- boolean sendExtraCommand(String command, Bundle extras);
- void addListener(int uid);
- void removeListener(int uid);
+ public String getName();
+
+ public void enable();
+ public void disable();
+ public boolean isEnabled();
+ public void setRequest(ProviderRequest request, WorkSource source);
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ // --- deprecated (but still supported) ---
+ public ProviderProperties getProperties();
+ public int getStatus(Bundle extras);
+ public long getStatusUpdateTime();
+ public boolean sendExtraCommand(String command, Bundle extras);
}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index a227ab6..7faf72c 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -16,458 +16,272 @@
package com.android.server.location;
-import android.content.ComponentName;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.location.Criteria;
-import android.location.ILocationProvider;
-import android.location.Location;
-import android.net.NetworkInfo;
+import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
-import com.android.internal.location.DummyLocationProvider;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ProviderRequest;
+import com.android.server.LocationManagerService;
+import com.android.server.ServiceWatcher;
/**
- * A class for proxying location providers implemented as services.
- *
- * {@hide}
+ * Proxy for ILocationProvider implementations.
*/
public class LocationProviderProxy implements LocationProviderInterface {
-
private static final String TAG = "LocationProviderProxy";
-
- public static final String SERVICE_ACTION =
- "com.android.location.service.NetworkLocationProvider";
+ private static final boolean D = LocationManagerService.D;
private final Context mContext;
private final String mName;
- private final Intent mIntent;
- private final Handler mHandler;
- private final Object mMutex = new Object(); // synchronizes access to non-final members
- private Connection mServiceConnection; // never null after ctor
+ private final ServiceWatcher mServiceWatcher;
+
+ private Object mLock = new Object();
- // cached values set by the location manager
- private boolean mLocationTracking = false;
+ // cached values set by the location manager, synchronized on mLock
+ private ProviderProperties mProperties;
private boolean mEnabled = false;
- private long mMinTime = -1;
- private WorkSource mMinTimeSource = new WorkSource();
- private int mNetworkState;
- private NetworkInfo mNetworkInfo;
-
- // constructor for proxying location providers implemented in a separate service
- public LocationProviderProxy(Context context, String name, String packageName,
- Handler handler) {
+ private ProviderRequest mRequest = null;
+ private WorkSource mWorksource = new WorkSource();
+
+ public static LocationProviderProxy createAndBind(Context context, String name, String action,
+ List<String> initialPackageNames, Handler handler) {
+ LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
+ initialPackageNames, handler);
+ if (proxy.bind()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private LocationProviderProxy(Context context, String name, String action,
+ List<String> initialPackageNames, Handler handler) {
mContext = context;
mName = name;
- mIntent = new Intent(SERVICE_ACTION);
- mHandler = handler;
- reconnect(packageName);
+ mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
+ mNewServiceWork, handler);
}
- /** Bind to service. Will reconnect if already connected */
- public void reconnect(String packageName) {
- synchronized (mMutex) {
- if (mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- }
- mServiceConnection = new Connection();
- mIntent.setPackage(packageName);
- mContext.bindService(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND |
- Context.BIND_ALLOW_OOM_MANAGEMENT);
- }
+ private boolean bind () {
+ return mServiceWatcher.start();
}
- private class Connection implements ServiceConnection, Runnable {
-
- private ILocationProvider mProvider;
-
- // for caching requiresNetwork, requiresSatellite, etc.
- private DummyLocationProvider mCachedAttributes; // synchronized by mMutex
+ private ILocationProvider getService() {
+ return ILocationProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
- public void onServiceConnected(ComponentName className, IBinder service) {
- synchronized (this) {
- mProvider = ILocationProvider.Stub.asInterface(service);
- if (mProvider != null) {
- mHandler.post(this);
- }
- }
- }
+ public String getConnectedPackageName() {
+ return mServiceWatcher.getBestPackageName();
+ }
- public void onServiceDisconnected(ComponentName className) {
- synchronized (this) {
- mProvider = null;
+ /**
+ * Work to apply current state to a newly connected provider.
+ * Remember we can switch the service that implements a providers
+ * at run-time, so need to apply current state.
+ */
+ private Runnable mNewServiceWork = new Runnable() {
+ @Override
+ public void run() {
+ if (D) Log.d(TAG, "applying state to connected service");
+
+ boolean enabled;
+ ProviderProperties properties = null;
+ ProviderRequest request;
+ WorkSource source;
+ ILocationProvider service;
+ synchronized (mLock) {
+ enabled = mEnabled;
+ request = mRequest;
+ source = mWorksource;
+ service = getService();
}
- }
- public synchronized ILocationProvider getProvider() {
- return mProvider;
- }
-
- public synchronized DummyLocationProvider getCachedAttributes() {
- return mCachedAttributes;
- }
+ if (service == null) return;
- public void run() {
- synchronized (mMutex) {
- if (mServiceConnection != this) {
- // This ServiceConnection no longer the one we want to bind to.
- return;
- }
- ILocationProvider provider = getProvider();
- if (provider == null) {
- return;
+ try {
+ // load properties from provider
+ properties = service.getProperties();
+ if (properties == null) {
+ Log.e(TAG, mServiceWatcher.getBestPackageName() +
+ " has invalid locatino provider properties");
}
- // resend previous values from the location manager if the service has restarted
- try {
- if (mEnabled) {
- provider.enable();
- }
- if (mLocationTracking) {
- provider.enableLocationTracking(true);
+ // apply current state to new service
+ if (enabled) {
+ service.enable();
+ if (request != null) {
+ service.setRequest(request, source);
}
- if (mMinTime >= 0) {
- provider.setMinTime(mMinTime, mMinTimeSource);
- }
- if (mNetworkInfo != null) {
- provider.updateNetworkState(mNetworkState, mNetworkInfo);
- }
- } catch (RemoteException e) {
}
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
- // init cache of parameters
- if (mCachedAttributes == null) {
- try {
- mCachedAttributes = new DummyLocationProvider(mName, null);
- mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
- mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
- mCachedAttributes.setRequiresCell(provider.requiresCell());
- mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
- mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
- mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
- mCachedAttributes.setSupportsBearing(provider.supportsBearing());
- mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
- mCachedAttributes.setAccuracy(provider.getAccuracy());
- } catch (RemoteException e) {
- mCachedAttributes = null;
- }
- }
+ synchronized (mLock) {
+ mProperties = properties;
}
}
};
+ @Override
public String getName() {
return mName;
}
- private DummyLocationProvider getCachedAttributes() {
- synchronized (mMutex) {
- return mServiceConnection.getCachedAttributes();
- }
- }
-
- public boolean requiresNetwork() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresNetwork();
- } else {
- return false;
- }
- }
-
- public boolean requiresSatellite() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresSatellite();
- } else {
- return false;
- }
- }
-
- public boolean requiresCell() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresCell();
- } else {
- return false;
- }
- }
-
- public boolean hasMonetaryCost() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.hasMonetaryCost();
- } else {
- return false;
- }
- }
-
- public boolean supportsAltitude() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsAltitude();
- } else {
- return false;
- }
- }
-
- public boolean supportsSpeed() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsSpeed();
- } else {
- return false;
- }
- }
-
- public boolean supportsBearing() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsBearing();
- } else {
- return false;
- }
- }
-
- public int getPowerRequirement() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.getPowerRequirement();
- } else {
- return -1;
+ @Override
+ public ProviderProperties getProperties() {
+ synchronized (mLock) {
+ return mProperties;
}
}
- public int getAccuracy() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.getAccuracy();
- } else {
- return -1;
- }
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- return provider.meetsCriteria(criteria);
- } catch (RemoteException e) {
- }
- }
- }
- // default implementation if we lost connection to the provider
- if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
- (criteria.getAccuracy() < getAccuracy())) {
- return false;
- }
- int criteriaPower = criteria.getPowerRequirement();
- if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
- (criteriaPower < getPowerRequirement())) {
- return false;
- }
- if (criteria.isAltitudeRequired() && !supportsAltitude()) {
- return false;
- }
- if (criteria.isSpeedRequired() && !supportsSpeed()) {
- return false;
- }
- if (criteria.isBearingRequired() && !supportsBearing()) {
- return false;
- }
- return true;
- }
-
+ @Override
public void enable() {
- synchronized (mMutex) {
+ synchronized (mLock) {
mEnabled = true;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.enable();
- } catch (RemoteException e) {
- }
- }
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public void disable() {
- synchronized (mMutex) {
- mEnabled = false;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.disable();
- } catch (RemoteException e) {
- }
- }
+ try {
+ service.enable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public boolean isEnabled() {
- synchronized (mMutex) {
- return mEnabled;
+ @Override
+ public void disable() {
+ synchronized (mLock) {
+ mEnabled = false;
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public int getStatus(Bundle extras) {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
+ try {
+ service.disable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
- if (provider != null) {
- try {
- return provider.getStatus(extras);
- } catch (RemoteException e) {
- }
- }
- return 0;
}
- public long getStatusUpdateTime() {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
- if (provider != null) {
- try {
- return provider.getStatusUpdateTime();
- } catch (RemoteException e) {
- }
- }
- return 0;
- }
-
- public String getInternalState() {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
- if (provider != null) {
- try {
- return provider.getInternalState();
- } catch (RemoteException e) {
- Log.e(TAG, "getInternalState failed", e);
- }
+ @Override
+ public boolean isEnabled() {
+ synchronized (mLock) {
+ return mEnabled;
}
- return null;
}
- public boolean isLocationTracking() {
- synchronized (mMutex) {
- return mLocationTracking;
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ synchronized (mLock) {
+ mRequest = request;
+ mWorksource = source;
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public void enableLocationTracking(boolean enable) {
- synchronized (mMutex) {
- mLocationTracking = enable;
- if (!enable) {
- mMinTime = -1;
- mMinTimeSource.clear();
- }
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.enableLocationTracking(enable);
- } catch (RemoteException e) {
- }
- }
+ try {
+ service.setRequest(request, source);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public long getMinTime() {
- synchronized (mMutex) {
- return mMinTime;
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.append("REMOTE SERVICE");
+ pw.append(" name=").append(mName);
+ pw.append(" pkg=").append(mServiceWatcher.getBestPackageName());
+ pw.append(" version=").append("" + mServiceWatcher.getBestVersion());
+ pw.append('\n');
+
+ ILocationProvider service = getService();
+ if (service == null) {
+ pw.println("service down (null)");
+ return;
+ }
+ pw.flush();
+
+ try {
+ service.asBinder().dump(fd, args);
+ } catch (RemoteException e) {
+ pw.println("service down (RemoteException)");
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ pw.println("service down (Exception)");
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public void setMinTime(long minTime, WorkSource ws) {
- synchronized (mMutex) {
- mMinTime = minTime;
- mMinTimeSource.set(ws);
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.setMinTime(minTime, ws);
- } catch (RemoteException e) {
- }
- }
- }
+ @Override
+ public int getStatus(Bundle extras) {
+ ILocationProvider service = getService();
+ if (service == null) return LocationProvider.TEMPORARILY_UNAVAILABLE;
+
+ try {
+ return service.getStatus(extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ return LocationProvider.TEMPORARILY_UNAVAILABLE;
}
- public void updateNetworkState(int state, NetworkInfo info) {
- synchronized (mMutex) {
- mNetworkState = state;
- mNetworkInfo = info;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.updateNetworkState(state, info);
- } catch (RemoteException e) {
- }
- }
- }
- }
+ @Override
+ public long getStatusUpdateTime() {
+ ILocationProvider service = getService();
+ if (service == null) return 0;
- public void updateLocation(Location location) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.updateLocation(location);
- } catch (RemoteException e) {
- }
- }
+ try {
+ return service.getStatusUpdateTime();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
+ return 0;
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- return provider.sendExtraCommand(command, extras);
- } catch (RemoteException e) {
- }
- }
- }
- return false;
- }
+ ILocationProvider service = getService();
+ if (service == null) return false;
- public void addListener(int uid) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.addListener(uid);
- } catch (RemoteException e) {
- }
- }
- }
- }
-
- public void removeListener(int uid) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.removeListener(uid);
- } catch (RemoteException e) {
- }
- }
+ try {
+ return service.sendExtraCommand(command, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
+ return false;
}
-}
+ }
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 09d799f..36c43ff 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -20,15 +20,19 @@ import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
import android.util.PrintWriterPrinter;
+
+import java.io.FileDescriptor;
import java.io.PrintWriter;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
/**
* A mock location provider used by LocationManagerService to implement test providers.
*
@@ -36,60 +40,56 @@ import java.io.PrintWriter;
*/
public class MockProvider implements LocationProviderInterface {
private final String mName;
+ private final ProviderProperties mProperties;
private final ILocationManager mLocationManager;
- private final boolean mRequiresNetwork;
- private final boolean mRequiresSatellite;
- private final boolean mRequiresCell;
- private final boolean mHasMonetaryCost;
- private final boolean mSupportsAltitude;
- private final boolean mSupportsSpeed;
- private final boolean mSupportsBearing;
- private final int mPowerRequirement;
- private final int mAccuracy;
+
private final Location mLocation;
+ private final Bundle mExtras = new Bundle();
+
private int mStatus;
private long mStatusUpdateTime;
- private final Bundle mExtras = new Bundle();
private boolean mHasLocation;
private boolean mHasStatus;
private boolean mEnabled;
private static final String TAG = "MockProvider";
- public MockProvider(String name, ILocationManager locationManager,
- boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ public MockProvider(String name, ILocationManager locationManager,
+ ProviderProperties properties) {
+ if (properties == null) throw new NullPointerException("properties is null");
+
mName = name;
mLocationManager = locationManager;
- mRequiresNetwork = requiresNetwork;
- mRequiresSatellite = requiresSatellite;
- mRequiresCell = requiresCell;
- mHasMonetaryCost = hasMonetaryCost;
- mSupportsAltitude = supportsAltitude;
- mSupportsBearing = supportsBearing;
- mSupportsSpeed = supportsSpeed;
- mPowerRequirement = powerRequirement;
- mAccuracy = accuracy;
+ mProperties = properties;
mLocation = new Location(name);
}
+ @Override
public String getName() {
return mName;
}
+ @Override
+ public ProviderProperties getProperties() {
+ return mProperties;
+ }
+
+ @Override
public void disable() {
mEnabled = false;
}
+ @Override
public void enable() {
mEnabled = true;
}
+ @Override
public boolean isEnabled() {
return mEnabled;
}
+ @Override
public int getStatus(Bundle extras) {
if (mHasStatus) {
extras.clear();
@@ -100,75 +100,20 @@ public class MockProvider implements LocationProviderInterface {
}
}
+ @Override
public long getStatusUpdateTime() {
return mStatusUpdateTime;
}
- public int getAccuracy() {
- return mAccuracy;
- }
-
- public int getPowerRequirement() {
- return mPowerRequirement;
- }
-
- public boolean hasMonetaryCost() {
- return mHasMonetaryCost;
- }
-
- public boolean requiresCell() {
- return mRequiresCell;
- }
-
- public boolean requiresNetwork() {
- return mRequiresNetwork;
- }
-
- public boolean requiresSatellite() {
- return mRequiresSatellite;
- }
-
- public boolean supportsAltitude() {
- return mSupportsAltitude;
- }
-
- public boolean supportsBearing() {
- return mSupportsBearing;
- }
-
- public boolean supportsSpeed() {
- return mSupportsSpeed;
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
- (criteria.getAccuracy() < mAccuracy)) {
- return false;
- }
- int criteriaPower = criteria.getPowerRequirement();
- if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
- (criteriaPower < mPowerRequirement)) {
- return false;
- }
- if (criteria.isAltitudeRequired() && !mSupportsAltitude) {
- return false;
- }
- if (criteria.isSpeedRequired() && !mSupportsSpeed) {
- return false;
- }
- if (criteria.isBearingRequired() && !mSupportsBearing) {
- return false;
- }
- return true;
- }
-
public void setLocation(Location l) {
mLocation.set(l);
mHasLocation = true;
- try {
- mLocationManager.reportLocation(mLocation, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
+ if (mEnabled) {
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
}
}
@@ -191,34 +136,9 @@ public class MockProvider implements LocationProviderInterface {
mStatusUpdateTime = 0;
}
- public String getInternalState() {
- return null;
- }
-
- public void enableLocationTracking(boolean enable) {
- }
-
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public void setMinTime(long minTime, WorkSource ws) {
- }
-
- public void updateNetworkState(int state, NetworkInfo info) {
- }
-
- public void updateLocation(Location location) {
- }
-
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
-
- public void addListener(int uid) {
- }
-
- public void removeListener(int uid) {
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ dump(pw, "");
}
public void dump(PrintWriter pw, String prefix) {
@@ -231,4 +151,12 @@ public class MockProvider implements LocationProviderInterface {
pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
pw.println(prefix + "mExtras=" + mExtras);
}
+
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) { }
+
+ @Override
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
}
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index ea0d1b0..0ce21b7 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -16,17 +16,23 @@
package com.android.server.location;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
+
/**
* A passive location provider reports locations received from other providers
* for clients that want to listen passively without actually triggering
@@ -35,103 +41,63 @@ import android.util.Log;
* {@hide}
*/
public class PassiveProvider implements LocationProviderInterface {
-
private static final String TAG = "PassiveProvider";
+ private static final ProviderProperties PROPERTIES = new ProviderProperties(
+ false, false, false, false, false, false, false,
+ Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
+
private final ILocationManager mLocationManager;
- private boolean mTracking;
+ private boolean mReportLocation;
public PassiveProvider(ILocationManager locationManager) {
mLocationManager = locationManager;
}
+ @Override
public String getName() {
return LocationManager.PASSIVE_PROVIDER;
}
- public boolean requiresNetwork() {
- return false;
- }
-
- public boolean requiresSatellite() {
- return false;
- }
-
- public boolean requiresCell() {
- return false;
- }
-
- public boolean hasMonetaryCost() {
- return false;
- }
-
- public boolean supportsAltitude() {
- return false;
- }
-
- public boolean supportsSpeed() {
- return false;
- }
-
- public boolean supportsBearing() {
- return false;
- }
-
- public int getPowerRequirement() {
- return -1;
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- // We do not want to match the special passive provider based on criteria.
- return false;
- }
-
- public int getAccuracy() {
- return -1;
+ @Override
+ public ProviderProperties getProperties() {
+ return PROPERTIES;
}
+ @Override
public boolean isEnabled() {
return true;
}
+ @Override
public void enable() {
}
+ @Override
public void disable() {
}
+ @Override
public int getStatus(Bundle extras) {
- if (mTracking) {
+ if (mReportLocation) {
return LocationProvider.AVAILABLE;
} else {
return LocationProvider.TEMPORARILY_UNAVAILABLE;
}
}
+ @Override
public long getStatusUpdateTime() {
return -1;
}
- public String getInternalState() {
- return null;
- }
-
- public void enableLocationTracking(boolean enable) {
- mTracking = enable;
- }
-
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public void setMinTime(long minTime, WorkSource ws) {
- }
-
- public void updateNetworkState(int state, NetworkInfo info) {
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ mReportLocation = request.reportLocation;
}
public void updateLocation(Location location) {
- if (mTracking) {
+ if (mReportLocation) {
try {
// pass the location back to the location manager
mLocationManager.reportLocation(location, true);
@@ -141,13 +107,13 @@ public class PassiveProvider implements LocationProviderInterface {
}
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
return false;
}
- public void addListener(int uid) {
- }
-
- public void removeListener(int uid) {
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("mReportLocaiton=" + mReportLocation);
}
}
diff --git a/services/java/com/android/server/net/NetworkAlertObserver.java b/services/java/com/android/server/net/BaseNetworkObserver.java
index 0d1c3b2..8b2aa5d 100644
--- a/services/java/com/android/server/net/NetworkAlertObserver.java
+++ b/services/java/com/android/server/net/BaseNetworkObserver.java
@@ -19,26 +19,39 @@ package com.android.server.net;
import android.net.INetworkManagementEventObserver;
/**
+ * Base {@link INetworkManagementEventObserver} that provides no-op
+ * implementations which can be overridden.
+ *
* @hide
*/
-public abstract class NetworkAlertObserver extends INetworkManagementEventObserver.Stub {
+public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub {
@Override
public void interfaceStatusChanged(String iface, boolean up) {
- // ignored; interface changes come through ConnectivityService
+ // default no-op
}
@Override
public void interfaceRemoved(String iface) {
- // ignored; interface changes come through ConnectivityService
+ // default no-op
}
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
- // ignored; interface changes come through ConnectivityService
+ // default no-op
}
@Override
public void interfaceAdded(String iface) {
- // ignored; interface changes come through ConnectivityService
+ // default no-op
+ }
+
+ @Override
+ public void interfaceClassDataActivityChanged(String label, boolean active) {
+ // default no-op
+ }
+
+ @Override
+ public void limitReached(String limitName, String iface) {
+ // default no-op
}
}
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
new file mode 100644
index 0000000..541650e
--- /dev/null
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.Preconditions;
+import com.android.server.ConnectivityService;
+import com.android.server.connectivity.Vpn;
+
+/**
+ * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
+ * connected and kicks off VPN connection, managing any required {@code netd}
+ * firewall rules.
+ */
+public class LockdownVpnTracker {
+ private static final String TAG = "LockdownVpnTracker";
+
+ /** Number of VPN attempts before waiting for user intervention. */
+ private static final int MAX_ERROR_COUNT = 4;
+
+ private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
+
+ private final Context mContext;
+ private final INetworkManagementService mNetService;
+ private final ConnectivityService mConnService;
+ private final Vpn mVpn;
+ private final VpnProfile mProfile;
+
+ private final Object mStateLock = new Object();
+
+ private PendingIntent mResetIntent;
+
+ private String mAcceptedEgressIface;
+ private String mAcceptedIface;
+ private String mAcceptedSourceAddr;
+
+ private int mErrorCount;
+
+ public static boolean isEnabled() {
+ return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN);
+ }
+
+ public LockdownVpnTracker(Context context, INetworkManagementService netService,
+ ConnectivityService connService, Vpn vpn, VpnProfile profile) {
+ mContext = Preconditions.checkNotNull(context);
+ mNetService = Preconditions.checkNotNull(netService);
+ mConnService = Preconditions.checkNotNull(connService);
+ mVpn = Preconditions.checkNotNull(vpn);
+ mProfile = Preconditions.checkNotNull(profile);
+
+ final Intent intent = new Intent(ACTION_LOCKDOWN_RESET);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mResetIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ }
+
+ private BroadcastReceiver mResetReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reset();
+ }
+ };
+
+ /**
+ * Watch for state changes to both active egress network, kicking off a VPN
+ * connection when ready, or setting firewall rules once VPN is connected.
+ */
+ private void handleStateChangedLocked() {
+ Slog.d(TAG, "handleStateChanged()");
+
+ final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
+ final LinkProperties egressProp = mConnService.getActiveLinkProperties();
+
+ final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
+ final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();
+
+ // Restart VPN when egress network disconnected or changed
+ final boolean egressDisconnected = egressInfo == null
+ || State.DISCONNECTED.equals(egressInfo.getState());
+ final boolean egressChanged = egressProp == null
+ || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
+ if (egressDisconnected || egressChanged) {
+ clearSourceRules();
+ mAcceptedEgressIface = null;
+ mVpn.stopLegacyVpn();
+ }
+ if (egressDisconnected) return;
+
+ if (mErrorCount > MAX_ERROR_COUNT) {
+ showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+
+ } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
+ if (mProfile.isValidLockdownProfile()) {
+ Slog.d(TAG, "Active network connected; starting VPN");
+ showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
+
+ mAcceptedEgressIface = egressProp.getInterfaceName();
+ mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp);
+
+ } else {
+ Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
+ showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+ }
+
+ } else if (vpnInfo.isConnected() && vpnConfig != null) {
+ final String iface = vpnConfig.interfaze;
+ final String sourceAddr = vpnConfig.addresses;
+
+ if (TextUtils.equals(iface, mAcceptedIface)
+ && TextUtils.equals(sourceAddr, mAcceptedSourceAddr)) {
+ return;
+ }
+
+ Slog.d(TAG, "VPN connected using iface=" + iface + ", sourceAddr=" + sourceAddr);
+ showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
+
+ try {
+ clearSourceRules();
+
+ mNetService.setFirewallInterfaceRule(iface, true);
+ mNetService.setFirewallEgressSourceRule(sourceAddr, true);
+
+ mErrorCount = 0;
+ mAcceptedIface = iface;
+ mAcceptedSourceAddr = sourceAddr;
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+
+ mConnService.sendConnectedBroadcast(augmentNetworkInfo(egressInfo));
+ }
+ }
+
+ public void init() {
+ Slog.d(TAG, "init()");
+
+ mVpn.setEnableNotifications(false);
+
+ final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
+ mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
+
+ try {
+ // TODO: support non-standard port numbers
+ mNetService.setFirewallEgressDestRule(mProfile.server, 500, true);
+ mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ public void shutdown() {
+ Slog.d(TAG, "shutdown()");
+
+ mAcceptedEgressIface = null;
+ mErrorCount = 0;
+
+ mVpn.stopLegacyVpn();
+ try {
+ mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
+ mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+ clearSourceRules();
+ hideNotification();
+
+ mContext.unregisterReceiver(mResetReceiver);
+ mVpn.setEnableNotifications(true);
+ }
+
+ public void reset() {
+ // cycle tracker, reset error count, and trigger retry
+ shutdown();
+ init();
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ private void clearSourceRules() {
+ try {
+ if (mAcceptedIface != null) {
+ mNetService.setFirewallInterfaceRule(mAcceptedIface, false);
+ mAcceptedIface = null;
+ }
+ if (mAcceptedSourceAddr != null) {
+ mNetService.setFirewallEgressSourceRule(mAcceptedSourceAddr, false);
+ mAcceptedSourceAddr = null;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+ }
+
+ public void onNetworkInfoChanged(NetworkInfo info) {
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ public void onVpnStateChanged(NetworkInfo info) {
+ if (info.getDetailedState() == DetailedState.FAILED) {
+ mErrorCount++;
+ }
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ public NetworkInfo augmentNetworkInfo(NetworkInfo info) {
+ final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
+ info = new NetworkInfo(info);
+ info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
+ return info;
+ }
+
+ private void showNotification(int titleRes, int iconRes) {
+ final Notification.Builder builder = new Notification.Builder(mContext);
+ builder.setWhen(0);
+ builder.setSmallIcon(iconRes);
+ builder.setContentTitle(mContext.getString(titleRes));
+ builder.setContentText(mContext.getString(R.string.vpn_lockdown_reset));
+ builder.setContentIntent(mResetIntent);
+ builder.setPriority(Notification.PRIORITY_LOW);
+ builder.setOngoing(true);
+ NotificationManager.from(mContext).notify(TAG, 0, builder.build());
+ }
+
+ private void hideNotification() {
+ NotificationManager.from(mContext).cancel(TAG, 0);
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index fe43d11..a7cba5a 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -112,11 +112,13 @@ import android.os.Message;
import android.os.MessageQueue.IdleHandler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.format.Formatter;
import android.text.format.Time;
+import android.util.AtomicFile;
import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.Slog;
@@ -127,7 +129,6 @@ import android.util.TrustedTime;
import android.util.Xml;
import com.android.internal.R;
-import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Objects;
@@ -425,7 +426,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final String action = intent.getAction();
final int uid = intent.getIntExtra(EXTRA_UID, 0);
- final int appId = UserId.getAppId(uid);
+ final int appId = UserHandle.getAppId(uid);
synchronized (mRulesLock) {
if (ACTION_PACKAGE_ADDED.equals(action)) {
// NOTE: PACKAGE_ADDED is currently only sent once, and is
@@ -570,7 +571,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/**
* Observer that watches for {@link INetworkManagementService} alerts.
*/
- private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() {
+ private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
@Override
public void limitReached(String limitName, String iface) {
// only someone like NMS should be calling us
@@ -1187,8 +1188,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int uid = readIntAttribute(in, ATTR_UID);
final int policy = readIntAttribute(in, ATTR_POLICY);
- final int appId = UserId.getAppId(uid);
- if (UserId.isApp(appId)) {
+ final int appId = UserHandle.getAppId(uid);
+ if (UserHandle.isApp(appId)) {
setAppPolicyUnchecked(appId, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
@@ -1197,7 +1198,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int appId = readIntAttribute(in, ATTR_APP_ID);
final int policy = readIntAttribute(in, ATTR_POLICY);
- if (UserId.isApp(appId)) {
+ if (UserHandle.isApp(appId)) {
setAppPolicyUnchecked(appId, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to appId " + appId + "; ignoring");
@@ -1303,7 +1304,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public void setAppPolicy(int appId, int policy) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- if (!UserId.isApp(appId)) {
+ if (!UserHandle.isApp(appId)) {
throw new IllegalArgumentException("cannot apply policy to appId " + appId);
}
@@ -1697,7 +1698,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final PackageManager pm = mContext.getPackageManager();
final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
for (ApplicationInfo app : apps) {
- final int appId = UserId.getAppId(app.uid);
+ final int appId = UserHandle.getAppId(app.uid);
updateRulesForAppLocked(appId);
}
@@ -1707,8 +1708,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
private void updateRulesForAppLocked(int appId) {
- for (UserInfo user : mContext.getPackageManager().getUsers()) {
- final int uid = UserId.getUid(user.id, appId);
+ UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ for (UserInfo user : um.getUsers()) {
+ final int uid = UserHandle.getUid(user.id, appId);
updateRulesForUidLocked(uid);
}
}
@@ -1716,7 +1718,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static boolean isUidValidForRules(int uid) {
// allow rules on specific system services, and any apps
if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID
- || UserId.isApp(uid)) {
+ || UserHandle.isApp(uid)) {
return true;
}
@@ -1726,7 +1728,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void updateRulesForUidLocked(int uid) {
if (!isUidValidForRules(uid)) return;
- final int appId = UserId.getAppId(uid);
+ final int appId = UserHandle.getAppId(uid);
final int appPolicy = getAppPolicy(appId);
final boolean uidForeground = isUidForeground(uid);
diff --git a/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java
index 9ddf011..60666b4 100644
--- a/services/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/java/com/android/server/net/NetworkStatsCollection.java
@@ -29,8 +29,8 @@ import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.text.format.DateUtils;
+import android.util.AtomicFile;
-import com.android.internal.os.AtomicFile;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Objects;
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index ba122ec..ffe2a3d 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -763,7 +763,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
/**
* Observer that watches for {@link INetworkManagementService} alerts.
*/
- private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() {
+ private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
@Override
public void limitReached(String limitName, String iface) {
// only someone like NMS should be calling us
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 48004bb..d4fe3fb 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -338,12 +338,14 @@ class Installer {
return execute(builder.toString());
}
- public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath,
+ public int getSizeInfo(String pkgName, int persona, String apkPath, String fwdLockApkPath,
String asecPath, PackageStats pStats) {
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
builder.append(' ');
+ builder.append(persona);
+ builder.append(' ');
builder.append(apkPath);
builder.append(' ');
builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index f914271..b84e25a 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -64,10 +64,13 @@ import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageStats;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
@@ -76,8 +79,8 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
-import android.content.pm.UserInfo;
import android.content.pm.ManifestDigest;
+import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.net.Uri;
@@ -100,7 +103,7 @@ import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
@@ -165,6 +168,7 @@ public class PackageManagerService extends IPackageManager.Stub {
static final boolean DEBUG_UPGRADE = false;
private static final boolean DEBUG_INSTALL = false;
private static final boolean DEBUG_REMOVE = false;
+ private static final boolean DEBUG_BROADCASTS = false;
private static final boolean DEBUG_SHOW_INFO = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
private static final boolean DEBUG_INTENT_MATCHING = false;
@@ -175,6 +179,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
+ private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
private static final boolean GET_CERTIFICATES = true;
@@ -210,7 +215,15 @@ public class PackageManagerService extends IPackageManager.Stub {
* The default maximum time to wait for the verification agent to return in
* milliseconds.
*/
- private static final long DEFAULT_VERIFICATION_TIMEOUT = 60 * 1000;
+ private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
+
+ /**
+ * The default response for package verification timeout.
+ *
+ * This can be either PackageManager.VERIFICATION_ALLOW or
+ * PackageManager.VERIFICATION_REJECT.
+ */
+ private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
@@ -388,6 +401,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// package uri's from external media onto secure containers
// or internal storage.
private IMediaContainerService mContainerService = null;
+ private int mContainerServiceUserId;
static final int SEND_PENDING_BROADCAST = 1;
static final int MCS_BOUND = 3;
@@ -410,7 +424,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
- static UserManager sUserManager;
+ static UserManagerService sUserManager;
// Stores a list of users whose package restrictions file needs to be updated
private HashSet<Integer> mDirtyUsers = new HashSet<Integer>();
@@ -456,8 +470,15 @@ public class PackageManagerService extends IPackageManager.Stub {
" DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ mContainerServiceUserId = 0;
+ if (mPendingInstalls.size() > 0) {
+ mContainerServiceUserId = mPendingInstalls.get(0).getUser().getIdentifier();
+ if (mContainerServiceUserId == UserHandle.USER_ALL) {
+ mContainerServiceUserId = 0;
+ }
+ }
if (mContext.bindService(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE)) {
+ Context.BIND_AUTO_CREATE, mContainerServiceUserId)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
@@ -534,6 +555,15 @@ public class PackageManagerService extends IPackageManager.Stub {
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
+ // Check if we're connected to the correct service, if it's an install
+ // request.
+ final int installFor = params.getUser().getIdentifier();
+ if (installFor != mContainerServiceUserId
+ && (installFor == UserHandle.USER_ALL
+ && mContainerServiceUserId != 0)) {
+ mHandler.sendEmptyMessage(MCS_RECONNECT);
+ return;
+ }
if (params.startCopy()) {
// We are done... look for more work or to
// go idle.
@@ -650,15 +680,21 @@ public class PackageManagerService extends IPackageManager.Stub {
break;
}
case START_CLEANING_PACKAGE: {
- String packageName = (String)msg.obj;
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ PackageCleanItem item = new PackageCleanItem((String)msg.obj,
+ msg.arg2 != 0);
synchronized (mPackages) {
- if (!mSettings.mPackagesToBeCleaned.contains(packageName)) {
- mSettings.mPackagesToBeCleaned.add(packageName);
+ if (msg.arg1 == UserHandle.USER_ALL) {
+ int[] users = sUserManager.getUserIds();
+ for (int user : users) {
+ mSettings.addPackageToCleanLPw(user, item);
+ }
+ } else {
+ mSettings.addPackageToCleanLPw(msg.arg1, item);
}
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- startCleaningPackages();
+ startCleaningPackages(-1);
} break;
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
@@ -680,20 +716,23 @@ public class PackageManagerService extends IPackageManager.Stub {
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, res.users);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, res.users);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
- res.pkg.applicationInfo.packageName, null,
- UserId.USER_ALL);
+ res.pkg.applicationInfo.packageName, null, res.users);
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
deleteOld = true;
}
+
+ // Log current value of "unknown sources" setting
+ EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
+ getUnknownSourcesSettings());
}
// Force a gc to clear up things
Runtime.getRuntime().gc();
@@ -764,17 +803,28 @@ public class PackageManagerService extends IPackageManager.Stub {
final int verificationId = msg.arg1;
final PackageVerificationState state = mPendingVerification.get(verificationId);
- if (state != null) {
+ if ((state != null) && !state.timeoutExtended()) {
final InstallArgs args = state.getInstallArgs();
Slog.i(TAG, "Verification timed out for " + args.packageURI.toString());
mPendingVerification.remove(verificationId);
- int ret = PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT;
- processPendingInstall(args, ret);
+ int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+
+ if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
+ Slog.i(TAG, "Continuing with installation of "
+ + args.packageURI.toString());
+ state.setVerifierResponse(Binder.getCallingUid(),
+ PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
+ try {
+ ret = args.copyApk(mContainerService, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not contact the ContainerService");
+ }
+ }
+ processPendingInstall(args, ret);
mHandler.sendEmptyMessage(MCS_UNBIND);
}
-
break;
}
case PACKAGE_VERIFIED: {
@@ -874,12 +924,13 @@ public class PackageManagerService extends IPackageManager.Stub {
mOnlyCore = onlyCore;
mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
- mSettings = new Settings();
+ mSettings = new Settings(context);
mSettings.addSharedUserLPw("android.uid.system",
Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -916,11 +967,12 @@ public class PackageManagerService extends IPackageManager.Stub {
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- sUserManager = new UserManager(mInstaller, mUserAppDataDir);
+ sUserManager = new UserManagerService(context, this,
+ mInstallLock, mPackages);
readPermissions();
- mRestoredSettings = mSettings.readLPw(getUsers());
+ mRestoredSettings = mSettings.readLPw(sUserManager.getUsers());
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
@@ -1110,8 +1162,7 @@ public class PackageManagerService extends IPackageManager.Stub {
String msg = "System package " + ps.name
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
- mInstaller.remove(ps.name, 0);
- sUserManager.removePackageForAllUsers(ps.name);
+ removeDataDirsLI(ps.name);
} else {
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
@@ -1160,9 +1211,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (deletedPkg == null) {
msg = "Updated system package " + deletedAppName
+ " no longer exists; wiping its data";
-
- mInstaller.remove(deletedAppName, 0);
- sUserManager.removePackageForAllUsers(deletedAppName);
+ removeDataDirsLI(deletedAppName);
} else {
msg = "Updated system app + " + deletedAppName
+ " no longer present; removing system privileges for "
@@ -1297,13 +1346,7 @@ public class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
- int retCode = mInstaller.remove(ps.name, 0);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove app data directory for package: "
- + ps.name + ", retcode=" + retCode);
- } else {
- sUserManager.removePackageForAllUsers(ps.name);
- }
+ removeDataDirsLI(ps.name);
if (ps.codePath != null) {
if (!ps.codePath.delete()) {
Slog.w(TAG, "Unable to remove old code file: " + ps.codePath);
@@ -1533,19 +1576,17 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
PackageInfo pi;
- if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- // The package has been uninstalled but has retained data and resources.
- pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId);
- } else {
- final PackageSetting ps = (PackageSetting) p.mExtras;
- if (ps == null) {
- return null;
- }
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
- ps.getStopped(userId), ps.getEnabled(userId), userId);
- pi.applicationInfo.enabledSetting = ps.getEnabled(userId);
+ final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (ps == null) {
+ return null;
+ }
+ final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+ final PackageUserState state = ps.readUserState(userId);
+ pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+ state, userId);
+ if (pi != null) {
+ pi.applicationInfo.enabledSetting = state.enabled;
pi.applicationInfo.enabled =
pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT
|| pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
@@ -1602,14 +1643,14 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if(p != null) {
- return UserId.getUid(userId, p.applicationInfo.uid);
+ return UserHandle.getUid(userId, p.applicationInfo.uid);
}
PackageSetting ps = mSettings.mPackages.get(packageName);
if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) {
return -1;
}
p = ps.pkg;
- return p != null ? UserId.getUid(userId, p.applicationInfo.uid) : -1;
+ return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1;
}
}
@@ -1712,14 +1753,15 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (ps.pkg == null) {
- PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags, userId);
+ PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName,
+ flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
}
- return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateApplicationInfo(ps.pkg, flags,
+ ps.readUserState(userId), userId);
}
return null;
}
@@ -1729,20 +1771,23 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
- PackageParser.Package pkg = new PackageParser.Package(packageName);
- if (ps.pkg == null) {
- ps.pkg = new PackageParser.Package(packageName);
- ps.pkg.applicationInfo.packageName = packageName;
- ps.pkg.applicationInfo.flags = ps.pkgFlags;
- ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
- ps.pkg.applicationInfo.sourceDir = ps.codePathString;
- ps.pkg.applicationInfo.dataDir =
+ PackageParser.Package pkg = ps.pkg;
+ if (pkg == null) {
+ if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
+ return null;
+ }
+ pkg = new PackageParser.Package(packageName);
+ pkg.applicationInfo.packageName = packageName;
+ pkg.applicationInfo.flags = ps.pkgFlags;
+ pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+ pkg.applicationInfo.sourceDir = ps.codePathString;
+ pkg.applicationInfo.dataDir =
getDataPathForPackage(ps.pkg.packageName, 0).getPath();
- ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+ pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
- // ps.pkg.mSetEnabled = ps.getEnabled(userId);
- // ps.pkg.mSetStopped = ps.getStopped(userId);
- return generatePackageInfo(ps.pkg, flags, userId);
+ // pkg.mSetEnabled = ps.getEnabled(userId);
+ // pkg.mSetStopped = ps.getStopped(userId);
+ return generatePackageInfo(pkg, flags, userId);
}
return null;
}
@@ -1760,13 +1805,12 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
// Note: isEnabledLP() does not apply here - always return info
- return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
- ps.getEnabled(userId));
+ return PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId));
}
if ("android".equals(packageName)||"system".equals(packageName)) {
return mAndroidApplication;
}
- if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+ if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
}
}
@@ -1782,9 +1826,11 @@ public class PackageManagerService extends IPackageManager.Stub {
public void run() {
mHandler.removeCallbacks(this);
int retCode = -1;
- retCode = mInstaller.freeCache(freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
+ synchronized (mInstallLock) {
+ retCode = mInstaller.freeCache(freeStorageSize);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't clear application caches");
+ }
}
if (observer != null) {
try {
@@ -1805,9 +1851,11 @@ public class PackageManagerService extends IPackageManager.Stub {
public void run() {
mHandler.removeCallbacks(this);
int retCode = -1;
- retCode = mInstaller.freeCache(freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
+ synchronized (mInstallLock) {
+ retCode = mInstaller.freeCache(freeStorageSize);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't clear application caches");
+ }
}
if(pi != null) {
try {
@@ -1833,8 +1881,8 @@ public class PackageManagerService extends IPackageManager.Stub {
if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+ userId);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
@@ -1853,8 +1901,8 @@ public class PackageManagerService extends IPackageManager.Stub {
if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+ userId);
}
}
return null;
@@ -1870,8 +1918,8 @@ public class PackageManagerService extends IPackageManager.Stub {
if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId),
+ userId);
}
}
return null;
@@ -1887,8 +1935,8 @@ public class PackageManagerService extends IPackageManager.Stub {
if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId),
+ userId);
}
}
return null;
@@ -1933,7 +1981,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private void checkValidCaller(int uid, int userId) {
- if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
+ if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
return;
throw new SecurityException("Caller uid=" + uid
@@ -1962,7 +2010,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
+ Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
@@ -1996,7 +2044,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (permName != null) {
BasePermission bp = findPermissionTreeLP(permName);
if (bp != null) {
- if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
+ if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid "
@@ -2199,8 +2247,8 @@ public class PackageManagerService extends IPackageManager.Stub {
public int checkUidSignatures(int uid1, int uid2) {
// Map to base uids.
- uid1 = UserId.getAppId(uid1);
- uid2 = UserId.getAppId(uid2);
+ uid1 = UserHandle.getAppId(uid1);
+ uid2 = UserHandle.getAppId(uid2);
// reader
synchronized (mPackages) {
Signature[] s1;
@@ -2258,7 +2306,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public String[] getPackagesForUid(int uid) {
- uid = UserId.getAppId(uid);
+ uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
@@ -2283,7 +2331,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public String getNameForUid(int uid) {
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
+ Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
@@ -2394,6 +2442,9 @@ public class PackageManagerService extends IPackageManager.Stub {
final int M = prefs.size();
for (int i=0; i<M; i++) {
final PreferredActivity pa = prefs.get(i);
+ if (pa.mUserId != userId) {
+ continue;
+ }
if (pa.mPref.mMatch != match) {
continue;
}
@@ -2763,7 +2814,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>();
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
final String[] keys;
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
// writer
synchronized (mPackages) {
@@ -2838,8 +2889,8 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null && ps != null) {
- ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ ai = PackageParser.generateApplicationInfo(p, flags,
+ ps.readUserState(userId), userId);
}
}
@@ -2862,17 +2913,20 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
PackageSetting ps = mSettings.mPackages.get(p.packageName);
- finalList.add(PackageParser.generateApplicationInfo(p, flags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId));
+ if (ps != null) {
+ ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
+ ps.readUserState(userId), userId);
+ if (ai != null) {
+ finalList.add(ai);
+ }
+ }
}
}
}
@@ -2889,14 +2943,12 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageSetting ps = provider != null
? mSettings.mPackages.get(provider.owner.packageName)
: null;
- return provider != null
+ return ps != null
&& mSettings.isEnabledLPr(provider.info, flags, userId)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
? PackageParser.generateProviderInfo(provider, flags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId)
+ ps.readUserState(userId), userId)
: null;
}
}
@@ -2910,20 +2962,21 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
.iterator();
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
PackageParser.Provider p = entry.getValue();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
- if (p.syncable
+ if (ps != null && p.syncable
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
- outNames.add(entry.getKey());
- outInfo.add(PackageParser.generateProviderInfo(p, 0,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId));
+ ProviderInfo info = PackageParser.generateProviderInfo(p, 0,
+ ps.readUserState(userId), userId);
+ if (info != null) {
+ outNames.add(entry.getKey());
+ outInfo.add(info);
+ }
}
}
}
@@ -2937,24 +2990,25 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
final int userId = processName != null ?
- UserId.getUserId(uid) : UserId.getCallingUserId();
+ UserHandle.getUserId(uid) : UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
- if (p.info.authority != null
+ if (ps != null && p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
- && UserId.isSameApp(p.info.applicationInfo.uid, uid)))
+ && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
&& mSettings.isEnabledLPr(p.info, flags, userId)
&& (!mSafeMode
|| (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p, flags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId));
+ ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
+ ps.readUserState(userId), userId);
+ if (info != null) {
+ finalList.add(info);
+ }
}
}
}
@@ -2987,8 +3041,11 @@ public class PackageManagerService extends IPackageManager.Stub {
final PackageParser.Instrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.info.targetPackage)) {
- finalList.add(PackageParser.generateInstrumentationInfo(p,
- flags));
+ InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p,
+ flags);
+ if (ii != null) {
+ finalList.add(ii);
+ }
}
}
}
@@ -3015,7 +3072,7 @@ public class PackageManagerService extends IPackageManager.Stub {
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
- flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
+ flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -3083,7 +3140,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* Returns null in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile,
- int parseFlags, int scanMode, long currentTime) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user) {
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
String scanPath = scanFile.getPath();
parseFlags |= mDefParseFlags;
@@ -3149,7 +3206,7 @@ public class PackageManagerService extends IPackageManager.Stub {
InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
- synchronized (mInstaller) {
+ synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
synchronized (mPackages) {
@@ -3183,7 +3240,7 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
- deletePackageLI(pkg.packageName, true, 0, null, false);
+ deletePackageLI(pkg.packageName, null, true, 0, null, false);
ps = null;
} else {
/*
@@ -3205,7 +3262,7 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
- synchronized (mInstaller) {
+ synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
}
@@ -3236,7 +3293,7 @@ public class PackageManagerService extends IPackageManager.Stub {
setApplicationInfoPaths(pkg, codePath, resPath);
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
- | SCAN_UPDATE_SIGNATURE, currentTime);
+ | SCAN_UPDATE_SIGNATURE, currentTime, user);
/*
* If the system app should be overridden by a previously installed
@@ -3439,8 +3496,38 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ private int createDataDirsLI(String packageName, int uid) {
+ int[] users = sUserManager.getUserIds();
+ int res = mInstaller.install(packageName, uid, uid);
+ if (res < 0) {
+ return res;
+ }
+ for (int user : users) {
+ if (user != 0) {
+ res = mInstaller.createUserData(packageName,
+ UserHandle.getUid(user, uid), user);
+ if (res < 0) {
+ return res;
+ }
+ }
+ }
+ return res;
+ }
+
+ private int removeDataDirsLI(String packageName) {
+ int[] users = sUserManager.getUserIds();
+ int res = 0;
+ for (int user : users) {
+ int resInner = mInstaller.remove(packageName, user);
+ if (resInner < 0) {
+ res = resInner;
+ }
+ }
+ return res;
+ }
+
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
- int parseFlags, int scanMode, long currentTime) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user) {
File scanFile = new File(pkg.mScanPath);
if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
@@ -3632,7 +3719,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryDir,
- pkg.applicationInfo.flags, true, false);
+ pkg.applicationInfo.flags, user, false);
if (pkgSetting == null) {
Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -3791,11 +3878,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|| (scanMode&SCAN_BOOTING) != 0)) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
- int ret = mInstaller.remove(pkgName, 0);
+ int ret = removeDataDirsLI(pkgName);
if (ret >= 0) {
// TODO: Kill the processes first
- // Remove the data directories for all users
- sUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
? "System package " : "Third party package ";
@@ -3807,8 +3892,7 @@ public class PackageManagerService extends IPackageManager.Stub {
recovered = true;
// And now re-install the app.
- ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.uid);
+ ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
if (ret == -1) {
// Ack should not happen!
msg = prefix + pkg.packageName
@@ -3817,9 +3901,6 @@ public class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
- // Create data directories for all users
- sUserManager.installPackageForAllUsers(pkgName,
- pkg.applicationInfo.uid);
}
if (!recovered) {
mHasSystemUidErrors = true;
@@ -3857,15 +3938,12 @@ public class PackageManagerService extends IPackageManager.Stub {
Log.v(TAG, "Want this data dir: " + dataPath);
}
//invoke installer to do the actual installation
- int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.uid);
+ int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
if (ret < 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
- // Create data directories for all users
- sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
@@ -3992,7 +4070,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
- mSettings.mPackagesToBeCleaned.remove(pkgName);
+ for (int i=0; i<mSettings.mPackagesToBeCleaned.size(); i++) {
+ mSettings.mPackagesToBeCleaned.valueAt(i).remove(pkgName);
+ }
// Take care of first install / last update times.
if (currentTime != 0) {
@@ -4716,14 +4796,17 @@ public class PackageManagerService extends IPackageManager.Stub {
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageActivities.size();
- ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut =
- new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N);
+ ArrayList<PackageParser.ActivityIntentInfo[]> listCut =
+ new ArrayList<PackageParser.ActivityIntentInfo[]>(N);
ArrayList<PackageParser.ActivityIntentInfo> intentFilters;
for (int i = 0; i < N; ++i) {
intentFilters = packageActivities.get(i).intents;
if (intentFilters != null && intentFilters.size() > 0) {
- listCut.add(intentFilters);
+ PackageParser.ActivityIntentInfo[] array =
+ new PackageParser.ActivityIntentInfo[intentFilters.size()];
+ intentFilters.toArray(array);
+ listCut.add(array);
}
}
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
@@ -4791,6 +4874,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
+ protected ActivityIntentInfo[] newArray(int size) {
+ return new ActivityIntentInfo[size];
+ }
+
+ @Override
protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) {
if (!sUserManager.exists(userId)) return true;
PackageParser.Package p = filter.activity.owner;
@@ -4800,7 +4888,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// System apps are never considered stopped for purposes of
// filtering, because there may be no way for the user to
// actually re-launch them.
- return ps.getStopped(userId) && (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0;
+ return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0
+ && ps.getStopped(userId);
}
}
return false;
@@ -4823,12 +4912,17 @@ public class PackageManagerService extends IPackageManager.Stub {
&ApplicationInfo.FLAG_SYSTEM) == 0) {
return null;
}
- final ResolveInfo res = new ResolveInfo();
PackageSetting ps = (PackageSetting) activity.owner.mExtras;
- res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId);
+ if (ps == null) {
+ return null;
+ }
+ ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags,
+ ps.readUserState(userId), userId);
+ if (ai == null) {
+ return null;
+ }
+ final ResolveInfo res = new ResolveInfo();
+ res.activityInfo = ai;
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
@@ -4904,14 +4998,17 @@ public class PackageManagerService extends IPackageManager.Stub {
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageServices.size();
- ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut =
- new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N);
+ ArrayList<PackageParser.ServiceIntentInfo[]> listCut =
+ new ArrayList<PackageParser.ServiceIntentInfo[]>(N);
ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
for (int i = 0; i < N; ++i) {
intentFilters = packageServices.get(i).intents;
if (intentFilters != null && intentFilters.size() > 0) {
- listCut.add(intentFilters);
+ PackageParser.ServiceIntentInfo[] array =
+ new PackageParser.ServiceIntentInfo[intentFilters.size()];
+ intentFilters.toArray(array);
+ listCut.add(array);
}
}
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
@@ -4974,6 +5071,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
+ protected PackageParser.ServiceIntentInfo[] newArray(int size) {
+ return new PackageParser.ServiceIntentInfo[size];
+ }
+
+ @Override
protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) {
if (!sUserManager.exists(userId)) return true;
PackageParser.Package p = filter.service.owner;
@@ -4983,8 +5085,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// System apps are never considered stopped for purposes of
// filtering, because there may be no way for the user to
// actually re-launch them.
- return ps.getStopped(userId)
- && (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0;
+ return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+ && ps.getStopped(userId);
}
}
return false;
@@ -5008,12 +5110,17 @@ public class PackageManagerService extends IPackageManager.Stub {
&ApplicationInfo.FLAG_SYSTEM) == 0) {
return null;
}
- final ResolveInfo res = new ResolveInfo();
PackageSetting ps = (PackageSetting) service.owner.mExtras;
- res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId);
+ if (ps == null) {
+ return null;
+ }
+ ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags,
+ ps.readUserState(userId), userId);
+ if (si == null) {
+ return null;
+ }
+ final ResolveInfo res = new ResolveInfo();
+ res.serviceInfo = si;
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
@@ -5104,13 +5211,14 @@ public class PackageManagerService extends IPackageManager.Stub {
};
static final void sendPackageBroadcast(String action, String pkg,
- Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) {
+ Bundle extras, String targetPkg, IIntentReceiver finishedReceiver,
+ int[] userIds) {
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- int[] userIds = userId == UserId.USER_ALL
- ? sUserManager.getUserIds()
- : new int[] {userId};
+ if (userIds == null) {
+ userIds = sUserManager.getUserIds();
+ }
for (int id : userIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts("package", pkg, null) : null);
@@ -5122,11 +5230,18 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// Modify the UID when posting to other users
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- if (uid > 0 && id > 0) {
- uid = UserId.getUid(id, UserId.getAppId(uid));
+ if (uid > 0 && UserHandle.getUserId(uid) != id) {
+ uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
intent.putExtra(Intent.EXTRA_UID, uid);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ if (DEBUG_BROADCASTS) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "Sending to user " + id + ": "
+ + intent.toShortString(false, true, false, false)
+ + " " + intent.getExtras(), here);
+ }
am.broadcastIntent(null, intent, null, finishedReceiver,
0, null, null, null, finishedReceiver != null, false, id);
}
@@ -5144,8 +5259,9 @@ public class PackageManagerService extends IPackageManager.Stub {
return mMediaMounted || Environment.isExternalStorageEmulated();
}
- public String nextPackageToClean(String lastPackage) {
+ public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) {
// writer
+ final int userId = UserHandle.getCallingUserId();
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
// If the external storage is no longer mounted at this point,
@@ -5153,34 +5269,65 @@ public class PackageManagerService extends IPackageManager.Stub {
// packages files and can not delete any more. Bail.
return null;
}
- if (lastPackage != null) {
- mSettings.mPackagesToBeCleaned.remove(lastPackage);
+ ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned.get(userId);
+ if (pkgs != null) {
+ if (lastPackage != null) {
+ pkgs.remove(lastPackage);
+ }
+ if (pkgs.size() > 0) {
+ return pkgs.get(0);
+ }
}
- return mSettings.mPackagesToBeCleaned.size() > 0
- ? mSettings.mPackagesToBeCleaned.get(0) : null;
}
+ // Move on to the next user to clean.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ startCleaningPackages(userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return null;
}
- void schedulePackageCleaning(String packageName) {
- mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName));
+ void schedulePackageCleaning(String packageName, int userId, boolean andCode) {
+ if (false) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId
+ + " andCode=" + andCode, here);
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,
+ userId, andCode ? 1 : 0, packageName));
}
- void startCleaningPackages() {
+ void startCleaningPackages(int lastUser) {
// reader
+ int nextUser = -1;
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
return;
}
- if (mSettings.mPackagesToBeCleaned.size() <= 0) {
+ final int N = mSettings.mPackagesToBeCleaned.size();
+ if (N <= 0) {
return;
}
+ for (int i=0; i<N; i++) {
+ int user = mSettings.mPackagesToBeCleaned.keyAt(i);
+ if (user > lastUser) {
+ nextUser = user;
+ break;
+ }
+ }
+ if (nextUser < 0) {
+ nextUser = mSettings.mPackagesToBeCleaned.keyAt(0);
+ }
}
Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- am.startService(null, intent, null);
+ am.startService(null, intent, null, nextUser);
} catch (RemoteException e) {
}
}
@@ -5243,7 +5390,7 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.PARSE_CHATTY |
PackageParser.PARSE_MUST_BE_APK,
SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
- System.currentTimeMillis());
+ System.currentTimeMillis(), null);
if (p != null) {
/*
* TODO this seems dangerous as the package may have
@@ -5272,13 +5419,13 @@ public class PackageManagerService extends IPackageManager.Stub {
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, null);
}
if (addedPackage != null) {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, addedUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, null);
}
}
@@ -5304,9 +5451,25 @@ public class PackageManagerService extends IPackageManager.Stub {
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+ VerificationParams verificationParams = new VerificationParams(verificationURI, null, null,
+ manifestDigest);
+ installPackageWithVerificationAndEncryption(packageURI, observer, flags,
+ installerPackageName, verificationParams, encryptionParams);
+ }
+
+ public void installPackageWithVerificationAndEncryption(Uri packageURI,
+ IPackageInstallObserver observer, int flags, String installerPackageName,
+ VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
+ null);
final int uid = Binder.getCallingUid();
+ UserHandle user;
+ if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
+ user = UserHandle.ALL;
+ } else {
+ user = Process.myUserHandle();
+ }
final int filteredFlags;
@@ -5321,10 +5484,51 @@ public class PackageManagerService extends IPackageManager.Stub {
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
- verificationURI, manifestDigest, encryptionParams);
+ verificationParams, encryptionParams, user);
mHandler.sendMessage(msg);
}
+ /**
+ * @hide
+ */
+ @Override
+ public int installExistingPackage(String packageName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
+ null);
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(uid);
+
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ boolean sendAdded = false;
+ Bundle extras = new Bundle(1);
+
+ // writer
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ return PackageManager.INSTALL_FAILED_INVALID_URI;
+ }
+ if (!pkgSetting.getInstalled(userId)) {
+ pkgSetting.setInstalled(true, userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+ extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
+ sendAdded = true;
+ }
+ }
+
+ if (sendAdded) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ packageName, extras, null, null, new int[] {userId});
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
@Override
public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
@@ -5335,6 +5539,32 @@ public class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
+ @Override
+ public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+ long millisecondsToDelay) {
+ final PackageVerificationState state = mPendingVerification.get(id);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, Binder.getCallingUid());
+
+ if ((millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT)
+ || (millisecondsToDelay < 0)) {
+ throw new IllegalArgumentException("millisecondsToDelay is out of bounds.");
+ }
+ if ((verificationCodeAtTimeout != PackageManager.VERIFICATION_ALLOW)
+ || (verificationCodeAtTimeout != PackageManager.VERIFICATION_REJECT)) {
+ throw new IllegalArgumentException("verificationCodeAtTimeout is unknown.");
+ }
+
+ if ((state != null) && !state.timeoutExtended()) {
+ state.extendTimeout();
+
+ final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessageDelayed(msg, millisecondsToDelay);
+ }
+ }
+
private ComponentName matchComponentForVerifier(String packageName,
List<ResolveInfo> receivers) {
ActivityInfo targetReceiver = null;
@@ -5453,6 +5683,17 @@ public class PackageManagerService extends IPackageManager.Stub {
}
/**
+ * Get the default verification agent response code.
+ *
+ * @return default verification response code
+ */
+ private int getDefaultVerificationResponse() {
+ return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
+ DEFAULT_VERIFICATION_RESPONSE);
+ }
+
+ /**
* Check whether or not package verification has been enabled.
*
* @return true if verification should be performed
@@ -5463,6 +5704,17 @@ public class PackageManagerService extends IPackageManager.Stub {
DEFAULT_VERIFY_ENABLE ? 1 : 0) == 1 ? true : false;
}
+ /**
+ * Get the "allow unknown sources" setting.
+ *
+ * @return the current "allow unknown sources" setting
+ */
+ private int getUnknownSourcesSettings() {
+ return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
+ -1);
+ }
+
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
final int uid = Binder.getCallingUid();
// writer
@@ -5615,6 +5867,17 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private int mRetries = 0;
+ /** User handle for the user requesting the information or installation. */
+ private final UserHandle mUser;
+
+ HandlerParams(UserHandle user) {
+ mUser = user;
+ }
+
+ UserHandle getUser() {
+ return mUser;
+ }
+
final boolean startCopy() {
boolean res;
try {
@@ -5656,6 +5919,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private final IPackageStatsObserver mObserver;
public MeasureParams(PackageStats stats, IPackageStatsObserver observer) {
+ super(new UserHandle(stats.userHandle));
mObserver = observer;
mStats = stats;
}
@@ -5663,7 +5927,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
void handleStartCopy() throws RemoteException {
synchronized (mInstallLock) {
- mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats);
+ mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats);
}
final boolean mounted;
@@ -5729,8 +5993,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private final Uri mPackageURI;
final String installerPackageName;
- final Uri verificationURI;
- final ManifestDigest manifestDigest;
+ final VerificationParams verificationParams;
private InstallArgs mArgs;
private int mRet;
private File mTempPackage;
@@ -5738,17 +6001,24 @@ public class PackageManagerService extends IPackageManager.Stub {
InstallParams(Uri packageURI,
IPackageInstallObserver observer, int flags,
- String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest,
- ContainerEncryptionParams encryptionParams) {
+ String installerPackageName, VerificationParams verificationParams,
+ ContainerEncryptionParams encryptionParams, UserHandle user) {
+ super(user);
this.mPackageURI = packageURI;
this.flags = flags;
this.observer = observer;
this.installerPackageName = installerPackageName;
- this.verificationURI = verificationURI;
- this.manifestDigest = manifestDigest;
+ this.verificationParams = verificationParams;
this.encryptionParams = encryptionParams;
}
+ public ManifestDigest getManifestDigest() {
+ if (verificationParams == null) {
+ return null;
+ }
+ return verificationParams.getManifestDigest();
+ }
+
private int installLocationPolicy(PackageInfoLite pkgLite, int flags) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
@@ -5758,6 +6028,16 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ // Check for downgrading.
+ if ((flags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) {
+ if (pkgLite.versionCode < pkg.mVersionCode) {
+ Slog.w(TAG, "Can't install update of " + packageName
+ + " update version " + pkgLite.versionCode
+ + " is older than installed version "
+ + pkg.mVersionCode);
+ return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
+ }
+ }
// Check for updated system application.
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (onSd) {
@@ -5884,6 +6164,8 @@ public class PackageManagerService extends IPackageManager.Stub {
ret = PackageManager.INSTALL_FAILED_INVALID_URI;
} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
+ ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
} else {
// Override with defaults if needed.
loc = installLocationPolicy(pkgLite, flags);
@@ -5919,8 +6201,9 @@ public class PackageManagerService extends IPackageManager.Stub {
verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final List<ResolveInfo> receivers = queryIntentReceivers(verification, null,
- PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */);
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification,
+ PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
+ 0 /* TODO: Which userId? */);
if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
@@ -5937,9 +6220,19 @@ public class PackageManagerService extends IPackageManager.Stub {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, flags);
- if (verificationURI != null) {
- verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI,
- verificationURI);
+ if (verificationParams != null) {
+ if (verificationParams.getVerificationURI() != null) {
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI,
+ verificationParams.getVerificationURI());
+ }
+ if (verificationParams.getOriginatingURI() != null) {
+ verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
+ verificationParams.getOriginatingURI());
+ }
+ if (verificationParams.getReferrer() != null) {
+ verification.putExtra(Intent.EXTRA_REFERRER,
+ verificationParams.getReferrer());
+ }
}
final PackageVerificationState verificationState = new PackageVerificationState(
@@ -6064,7 +6357,8 @@ public class PackageManagerService extends IPackageManager.Stub {
int mRet;
MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
- String packageName, String dataDir, int uid) {
+ String packageName, String dataDir, int uid, UserHandle user) {
+ super(user);
this.srcArgs = srcArgs;
this.observer = observer;
this.flags = flags;
@@ -6217,14 +6511,17 @@ public class PackageManagerService extends IPackageManager.Stub {
final Uri packageURI;
final String installerPackageName;
final ManifestDigest manifestDigest;
+ final UserHandle user;
InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName, ManifestDigest manifestDigest) {
+ String installerPackageName, ManifestDigest manifestDigest,
+ UserHandle user) {
this.packageURI = packageURI;
this.flags = flags;
this.observer = observer;
this.installerPackageName = installerPackageName;
this.manifestDigest = manifestDigest;
+ this.user = user;
}
abstract void createCopyFile();
@@ -6275,11 +6572,12 @@ public class PackageManagerService extends IPackageManager.Stub {
FileInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.flags,
- params.installerPackageName, params.manifestDigest);
+ params.installerPackageName, params.getManifestDigest(),
+ params.getUser());
}
FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
- super(null, null, 0, null, null);
+ super(null, null, 0, null, null, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
@@ -6288,7 +6586,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
- super(packageURI, null, 0, null, null);
+ super(packageURI, null, 0, null, null, null);
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -6562,13 +6860,15 @@ public class PackageManagerService extends IPackageManager.Stub {
AsecInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.flags,
- params.installerPackageName, params.manifestDigest);
+ params.installerPackageName, params.getManifestDigest(),
+ params.getUser());
}
AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
boolean isExternal, boolean isForwardLocked) {
super(null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+ | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+ null, null, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@@ -6579,14 +6879,16 @@ public class PackageManagerService extends IPackageManager.Stub {
AsecInstallArgs(String cid, boolean isForwardLocked) {
super(null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+ | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+ null, null, null);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) {
super(packageURI, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+ | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+ null, null, null);
this.cid = cid;
}
@@ -6910,6 +7212,7 @@ public class PackageManagerService extends IPackageManager.Stub {
class PackageInstalledInfo {
String name;
int uid;
+ int[] users;
PackageParser.Package pkg;
int returnCode;
PackageRemovedInfo removedInfo;
@@ -6919,14 +7222,12 @@ public class PackageManagerService extends IPackageManager.Stub {
* Install a non-existing package.
*/
private void installNewPackageLI(PackageParser.Package pkg,
- int parseFlags,
- int scanMode,
+ int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
- res.name = pkgName;
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
// A package with the same name is already installed, though
@@ -6949,7 +7250,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
- System.currentTimeMillis());
+ System.currentTimeMillis(), user);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -6966,17 +7267,15 @@ public class PackageManagerService extends IPackageManager.Stub {
// delete the package data and cache directories that it created in
// scanPackageLocked, unless those directories existed before we even tried to
// install.
- deletePackageLI(
- pkgName, false,
- dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
+ deletePackageLI(pkgName, UserHandle.ALL, false,
+ dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
res.removedInfo, true);
}
}
}
private void replacePackageLI(PackageParser.Package pkg,
- int parseFlags,
- int scanMode,
+ int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
PackageParser.Package oldPackage;
@@ -6993,15 +7292,16 @@ public class PackageManagerService extends IPackageManager.Stub {
}
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
- replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+ replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+ user, installerPackageName, res);
} else {
- replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+ replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+ user, installerPackageName, res);
}
}
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
- PackageParser.Package pkg,
- int parseFlags, int scanMode,
+ PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
PackageParser.Package newPackage = null;
String pkgName = deletedPackage.packageName;
@@ -7016,7 +7316,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// First delete the existing package while retaining the data directory
- if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA,
+ if (!deletePackageLI(pkgName, null, true, PackageManager.DELETE_KEEP_DATA,
res.removedInfo, true)) {
// If the existing package wasn't successfully deleted
res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -7025,7 +7325,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Successfully deleted the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
- System.currentTimeMillis());
+ System.currentTimeMillis(), user);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7046,8 +7346,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// install.
if(updatedSettings) {
deletePackageLI(
- pkgName, true,
- PackageManager.DONT_DELETE_DATA,
+ pkgName, null, true,
+ PackageManager.DELETE_KEEP_DATA,
res.removedInfo, true);
}
// Since we failed to install the new package we need to restore the old
@@ -7062,7 +7362,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
| SCAN_UPDATE_TIME;
if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
- origUpdateTime) == null) {
+ origUpdateTime, user) == null) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
return;
}
@@ -7080,8 +7380,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
- PackageParser.Package pkg,
- int parseFlags, int scanMode,
+ PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
PackageParser.Package newPackage = null;
boolean updatedSettings = false;
@@ -7130,7 +7429,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Successfully disabled the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0);
+ newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7153,7 +7452,7 @@ public class PackageManagerService extends IPackageManager.Stub {
removePackageLI(newPackage, true);
}
// Add back the old system package
- scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0);
+ scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
// Restore the old system information in Settings
synchronized(mPackages) {
if (updatedSettings) {
@@ -7212,6 +7511,10 @@ public class PackageManagerService extends IPackageManager.Stub {
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
res.name = pkgName;
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+ }
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
@@ -7327,12 +7630,18 @@ public class PackageManagerService extends IPackageManager.Stub {
setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
- replacePackageLI(pkg, parseFlags, scanMode,
+ replacePackageLI(pkg, parseFlags, scanMode, args.user,
installerPackageName, res);
} else {
- installNewPackageLI(pkg, parseFlags, scanMode,
+ installNewPackageLI(pkg, parseFlags, scanMode, args.user,
installerPackageName,res);
}
+ synchronized (mPackages) {
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+ }
+ }
}
private static boolean isForwardLocked(PackageParser.Package pkg) {
@@ -7423,10 +7732,11 @@ public class PackageManagerService extends IPackageManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
// Queue up an async operation since the package deletion may take a little while.
+ final int uid = Binder.getCallingUid();
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- final int returnCode = deletePackageX(packageName, true, true, flags);
+ final int returnCode = deletePackageX(packageName, uid, flags);
if (observer != null) {
try {
observer.packageDeleted(packageName, returnCode);
@@ -7452,8 +7762,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* persisting settings for later use
* sending a broadcast if necessary
*/
- private int deletePackageX(String packageName, boolean sendBroadCast,
- boolean deleteCodeAndResources, int flags) {
+ private int deletePackageX(String packageName, int uid, int flags) {
final PackageRemovedInfo info = new PackageRemovedInfo();
final boolean res;
@@ -7468,27 +7777,30 @@ public class PackageManagerService extends IPackageManager.Stub {
}
synchronized (mInstallLock) {
- res = deletePackageLI(packageName, deleteCodeAndResources,
- flags | REMOVE_CHATTY, info, true);
+ res = deletePackageLI(packageName,
+ (flags & PackageManager.DELETE_ALL_USERS) != 0
+ ? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)),
+ true, flags | REMOVE_CHATTY, info, true);
}
- if (res && sendBroadCast) {
+ if (res) {
boolean systemUpdate = info.isRemovedPackageSystemUpdate;
- info.sendBroadcast(deleteCodeAndResources, systemUpdate);
+ info.sendBroadcast(true, systemUpdate);
// If the removed package was a system update, the old system packaged
// was re-enabled; we need to broadcast this information
if (systemUpdate) {
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid);
+ extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
+ ? info.removedAppId : info.uid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, null);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, null);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, packageName, null, UserId.USER_ALL);
+ null, packageName, null, null);
}
}
// Force a gc here.
@@ -7497,7 +7809,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// other processes clean up before deleting resources.
if (info.args != null) {
synchronized (mInstallLock) {
- info.args.doPostDeleteLI(deleteCodeAndResources);
+ info.args.doPostDeleteLI(true);
}
}
@@ -7507,29 +7819,30 @@ public class PackageManagerService extends IPackageManager.Stub {
static class PackageRemovedInfo {
String removedPackage;
int uid = -1;
- int removedUid = -1;
+ int removedAppId = -1;
+ int[] removedUsers = null;
boolean isRemovedPackageSystemUpdate = false;
// Clean up resources deleted packages.
InstallArgs args = null;
void sendBroadcast(boolean fullRemove, boolean replacing) {
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid);
+ extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove);
if (replacing) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
if (removedPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, removedUsers);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, removedUsers);
}
}
- if (removedUid >= 0) {
+ if (removedAppId >= 0) {
sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
- UserId.getUserId(removedUid));
+ removedUsers);
}
}
}
@@ -7543,34 +7856,28 @@ public class PackageManagerService extends IPackageManager.Stub {
private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo,
int flags, boolean writeSettings) {
String packageName = p.packageName;
- if (outInfo != null) {
- outInfo.removedPackage = packageName;
- }
removePackageLI(p, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
final PackageSetting deletedPs;
// reader
synchronized (mPackages) {
deletedPs = mSettings.mPackages.get(packageName);
- }
- if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
- int retCode = mInstaller.remove(packageName, 0);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
- + packageName + ", retcode=" + retCode);
- // we don't consider this to be a failure of the core package deletion
- } else {
- // TODO: Kill the processes first
- sUserManager.removePackageForAllUsers(packageName);
+ if (outInfo != null) {
+ outInfo.removedPackage = packageName;
+ outInfo.removedUsers = deletedPs != null
+ ? deletedPs.getInstalledUsers(sUserManager.getUserIds()) : null;
}
- schedulePackageCleaning(packageName);
+ }
+ if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
+ removeDataDirsLI(packageName);
+ schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
}
// writer
synchronized (mPackages) {
if (deletedPs != null) {
- if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+ if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
if (outInfo != null) {
- outInfo.removedUid = mSettings.removePackageLPw(packageName);
+ outInfo.removedAppId = mSettings.removePackageLPw(packageName);
}
if (deletedPs != null) {
updatePermissionsLPw(deletedPs.name, null, 0);
@@ -7579,7 +7886,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
}
}
- clearPackagePreferredActivitiesLPw(deletedPs.name);
+ clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
}
}
// can downgrade to reader
@@ -7619,10 +7926,10 @@ public class PackageManagerService extends IPackageManager.Stub {
outInfo.isRemovedPackageSystemUpdate = true;
if (ps.versionCode < p.mVersionCode) {
// Delete data for downgrades
- flags &= ~PackageManager.DONT_DELETE_DATA;
+ flags &= ~PackageManager.DELETE_KEEP_DATA;
} else {
// Preserve data by setting flag
- flags |= PackageManager.DONT_DELETE_DATA;
+ flags |= PackageManager.DELETE_KEEP_DATA;
}
boolean ret = deleteInstalledPackageLI(p, true, flags, outInfo,
writeSettings);
@@ -7639,7 +7946,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Install the system package
PackageParser.Package newPkg = scanPackageLI(ps.codePath,
PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
- SCAN_MONITOR | SCAN_NO_PATHS, 0);
+ SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
if (newPkg == null) {
Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
@@ -7686,7 +7993,7 @@ public class PackageManagerService extends IPackageManager.Stub {
/*
* This method handles package deletion in general
*/
- private boolean deletePackageLI(String packageName,
+ private boolean deletePackageLI(String packageName, UserHandle user,
boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo,
boolean writeSettings) {
if (packageName == null) {
@@ -7695,6 +8002,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
PackageParser.Package p;
boolean dataOnly = false;
+ int removeUser = -1;
+ int appId = -1;
synchronized (mPackages) {
p = mPackages.get(packageName);
if (p == null) {
@@ -7702,15 +8011,53 @@ public class PackageManagerService extends IPackageManager.Stub {
dataOnly = true;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
+ Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
p = ps.pkg;
}
+ if (p == null) {
+ Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
+ return false;
+ }
+ final PackageSetting ps = (PackageSetting)p.mExtras;
+ if (!isSystemApp(p) && ps != null && user != null
+ && user.getIdentifier() != UserHandle.USER_ALL) {
+ // The caller is asking that the package only be deleted for a single
+ // user. To do this, we just mark its uninstalled state and delete
+ // its data.
+ ps.setUserState(user.getIdentifier(),
+ COMPONENT_ENABLED_STATE_DEFAULT,
+ false, //installed
+ true, //stopped
+ true, //notLaunched
+ null, null);
+ if (ps.isAnyInstalled(sUserManager.getUserIds())) {
+ // Other user still have this package installed, so all
+ // we need to do is clear this user's data and save that
+ // it is uninstalled.
+ removeUser = user.getIdentifier();
+ appId = ps.appId;
+ mSettings.writePackageRestrictionsLPr(removeUser);
+ } else {
+ // We need to set it back to 'installed' so the uninstall
+ // broadcasts will be sent correctly.
+ ps.setInstalled(true, user.getIdentifier());
+ }
+ }
}
- if (p == null) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
- return false;
+
+ if (removeUser >= 0) {
+ // From above, we determined that we are deleting this only
+ // for a single user. Continue the work here.
+ if (outInfo != null) {
+ outInfo.removedPackage = packageName;
+ outInfo.removedAppId = appId;
+ outInfo.removedUsers = new int[] {removeUser};
+ }
+ mInstaller.clearUserData(packageName, removeUser);
+ schedulePackageCleaning(packageName, removeUser, false);
+ return true;
}
if (dataOnly) {
@@ -7755,7 +8102,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private void clearExternalStorageDataSync(String packageName, boolean allData) {
+ private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) {
final boolean mounted;
if (Environment.isExternalStorageEmulated()) {
mounted = true;
@@ -7771,44 +8118,52 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final Intent containerIntent = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- ClearStorageConnection conn = new ClearStorageConnection();
- if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE)) {
- try {
- long timeout = SystemClock.uptimeMillis() + 5000;
- synchronized (conn) {
- long now = SystemClock.uptimeMillis();
- while (conn.mContainerService == null && now < timeout) {
- try {
- conn.wait(timeout - now);
- } catch (InterruptedException e) {
+ int[] users;
+ if (userId == UserHandle.USER_ALL) {
+ users = sUserManager.getUserIds();
+ } else {
+ users = new int[] { userId };
+ }
+ for (int curUser : users) {
+ ClearStorageConnection conn = new ClearStorageConnection();
+ if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE, curUser)) {
+ try {
+ long timeout = SystemClock.uptimeMillis() + 5000;
+ synchronized (conn) {
+ long now = SystemClock.uptimeMillis();
+ while (conn.mContainerService == null && now < timeout) {
+ try {
+ conn.wait(timeout - now);
+ } catch (InterruptedException e) {
+ }
}
}
- }
- if (conn.mContainerService == null) {
- return;
- }
- final File externalCacheDir = Environment
- .getExternalStorageAppCacheDirectory(packageName);
- try {
- conn.mContainerService.clearDirectory(externalCacheDir.toString());
- } catch (RemoteException e) {
- }
- if (allData) {
- final File externalDataDir = Environment
- .getExternalStorageAppDataDirectory(packageName);
- try {
- conn.mContainerService.clearDirectory(externalDataDir.toString());
- } catch (RemoteException e) {
+ if (conn.mContainerService == null) {
+ return;
}
- final File externalMediaDir = Environment
- .getExternalStorageAppMediaDirectory(packageName);
+ final File externalCacheDir = Environment
+ .getExternalStorageAppCacheDirectory(packageName);
try {
- conn.mContainerService.clearDirectory(externalMediaDir.toString());
+ conn.mContainerService.clearDirectory(externalCacheDir.toString());
} catch (RemoteException e) {
}
+ if (allData) {
+ final File externalDataDir = Environment
+ .getExternalStorageAppDataDirectory(packageName);
+ try {
+ conn.mContainerService.clearDirectory(externalDataDir.toString());
+ } catch (RemoteException e) {
+ }
+ final File externalMediaDir = Environment
+ .getExternalStorageAppMediaDirectory(packageName);
+ try {
+ conn.mContainerService.clearDirectory(externalMediaDir.toString());
+ } catch (RemoteException e) {
+ }
+ }
+ } finally {
+ mContext.unbindService(conn);
}
- } finally {
- mContext.unbindService(conn);
}
}
}
@@ -7827,7 +8182,7 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
succeeded = clearApplicationUserDataLI(packageName, userId);
}
- clearExternalStorageDataSync(packageName, true);
+ clearExternalStorageDataSync(packageName, userId, true);
if (succeeded) {
// invoke DeviceStorageMonitor's update method to clear any notifications
DeviceStorageMonitorService dsm = (DeviceStorageMonitorService)
@@ -7893,7 +8248,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_CACHE_FILES, null);
// Queue up an async operation since the package deletion may take a little while.
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
@@ -7901,7 +8256,7 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
succeded = deleteApplicationCacheFilesLI(packageName, userId);
}
- clearExternalStorageDataSync(packageName, false);
+ clearExternalStorageDataSync(packageName, userId, false);
if(observer != null) {
try {
observer.onRemoveCompleted(packageName, succeded);
@@ -7941,12 +8296,12 @@ public class PackageManagerService extends IPackageManager.Stub {
return true;
}
- public void getPackageSizeInfo(final String packageName,
+ public void getPackageSizeInfo(final String packageName, int userHandle,
final IPackageStatsObserver observer) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GET_PACKAGE_SIZE, null);
- PackageStats stats = new PackageStats(packageName);
+ PackageStats stats = new PackageStats(packageName, userHandle);
/*
* Queue up an async operation since the package measurement may take a
@@ -7957,7 +8312,8 @@ public class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
- private boolean getPackageSizeInfoLI(String packageName, PackageStats pStats) {
+ private boolean getPackageSizeInfoLI(String packageName, int userHandle,
+ PackageStats pStats) {
if (packageName == null) {
Slog.w(TAG, "Attempt to get size of null packageName.");
return false;
@@ -7994,7 +8350,7 @@ public class PackageManagerService extends IPackageManager.Stub {
publicSrcDir = applicationInfo.publicSourceDir;
}
}
- int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir,
+ int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, publicSrcDir,
asecPath, pStats);
if (res < 0) {
return false;
@@ -8046,26 +8402,28 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public void addPreferredActivity(IntentFilter filter, int match,
- ComponentName[] set, ComponentName activity) {
+ ComponentName[] set, ComponentName activity, int userId) {
// writer
+ int callingUid = Binder.getCallingUid();
+ checkValidCaller(callingUid, userId);
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(callingUid)
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
- + Binder.getCallingUid());
+ + callingUid);
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
-
- Slog.i(TAG, "Adding preferred activity " + activity + ":");
+
+ Slog.i(TAG, "Adding preferred activity " + activity + " for user " + userId + " :");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
mSettings.mPreferredActivities.addFilter(
- new PreferredActivity(filter, match, set, activity));
+ new PreferredActivity(filter, match, set, activity, userId));
scheduleWriteSettingsLocked();
}
}
@@ -8101,13 +8459,15 @@ public class PackageManagerService extends IPackageManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
-
+
+ final int callingUserId = UserHandle.getCallingUserId();
ArrayList<PreferredActivity> removed = null;
Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
String action = filter.getAction(0);
String category = filter.getCategory(0);
while (it.hasNext()) {
PreferredActivity pa = it.next();
+ if (pa.mUserId != callingUserId) continue;
if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) {
if (removed == null) {
removed = new ArrayList<PreferredActivity>();
@@ -8123,7 +8483,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.mPreferredActivities.removeFilter(pa);
}
}
- addPreferredActivity(filter, match, set, activity);
+ addPreferredActivity(filter, match, set, activity, callingUserId);
}
}
@@ -8147,17 +8507,21 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- if (clearPackagePreferredActivitiesLPw(packageName)) {
+ if (clearPackagePreferredActivitiesLPw(packageName, UserHandle.getCallingUserId())) {
scheduleWriteSettingsLocked();
}
}
}
- boolean clearPackagePreferredActivitiesLPw(String packageName) {
+ /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+ boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) {
ArrayList<PreferredActivity> removed = null;
Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
while (it.hasNext()) {
PreferredActivity pa = it.next();
+ if (userId != UserHandle.USER_ALL && pa.mUserId != userId) {
+ continue;
+ }
if (pa.mPref.mComponent.getPackageName().equals(packageName)) {
if (removed == null) {
removed = new ArrayList<PreferredActivity>();
@@ -8179,11 +8543,15 @@ public class PackageManagerService extends IPackageManager.Stub {
List<ComponentName> outActivities, String packageName) {
int num = 0;
+ final int userId = UserHandle.getCallingUserId();
// reader
synchronized (mPackages) {
final Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
while (it.hasNext()) {
final PreferredActivity pa = it.next();
+ if (pa.mUserId != userId) {
+ continue;
+ }
if (packageName == null
|| pa.mPref.mComponent.getPackageName().equals(packageName)) {
if (outFilters != null) {
@@ -8248,7 +8616,7 @@ public class PackageManagerService extends IPackageManager.Stub {
+ "/" + className);
}
// Allow root and verify that userId is not being specified by a different user
- if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.appId)) {
+ if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) {
throw new SecurityException(
"Permission Denial: attempt to change component state from pid="
+ Binder.getCallingPid()
@@ -8297,7 +8665,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
mSettings.writePackageRestrictionsLPr(userId);
- packageUid = UserId.getUid(userId, pkgSetting.appId);
+ packageUid = UserHandle.getUid(userId, pkgSetting.appId);
components = mPendingBroadcasts.get(packageName);
final boolean newPackage = components == null;
if (newPackage) {
@@ -8346,7 +8714,7 @@ public class PackageManagerService extends IPackageManager.Stub {
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
- UserId.getUserId(packageUid));
+ new int[] {UserHandle.getUserId(packageUid)});
}
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
@@ -8951,7 +9319,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Loading packages");
loadMediaPackages(processCids, uidArr, removeCids);
- startCleaningPackages();
+ startCleaningPackages(-1);
} else {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Unloading packages");
@@ -8972,7 +9340,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserId.USER_ALL);
+ sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
}
}
@@ -9017,7 +9385,7 @@ public class PackageManagerService extends IPackageManager.Stub {
doGc = true;
synchronized (mInstallLock) {
final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
- 0, 0);
+ 0, 0, null);
// Scan the package
if (pkg != null) {
/*
@@ -9126,8 +9494,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// Delete package internally
PackageRemovedInfo outInfo = new PackageRemovedInfo();
synchronized (mInstallLock) {
- boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
- outInfo, false);
+ boolean res = deletePackageLI(pkgName, null, false,
+ PackageManager.DELETE_KEEP_DATA, outInfo, false);
if (res) {
pkgList.add(pkgName);
} else {
@@ -9164,9 +9532,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ /** Binder call */
+ @Override
public void movePackage(final String packageName, final IPackageMoveObserver observer,
final int flags) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+ UserHandle user = new UserHandle(UserHandle.getCallingUserId());
int returnCode = PackageManager.MOVE_SUCCEEDED;
int currFlags = 0;
int newFlags = 0;
@@ -9217,14 +9588,15 @@ public class PackageManagerService extends IPackageManager.Stub {
* anyway.
*/
if (returnCode != PackageManager.MOVE_SUCCEEDED) {
- processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1),
+ processPendingMove(new MoveParams(null, observer, 0, packageName,
+ null, -1, user),
returnCode);
} else {
Message msg = mHandler.obtainMessage(INIT_COPY);
InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir);
MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
- pkg.applicationInfo.dataDir, pkg.applicationInfo.uid);
+ pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user);
msg.obj = mp;
mHandler.sendMessage(msg);
}
@@ -9415,48 +9787,37 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageHelper.APP_INSTALL_AUTO);
}
- public UserInfo createUser(String name, int flags) {
- // TODO(kroot): Add a real permission for creating users
- enforceSystemOrRoot("Only the system can create users");
-
- UserInfo userInfo = sUserManager.createUser(name, flags);
- if (userInfo != null) {
- Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
- }
- return userInfo;
- }
-
- public boolean removeUser(int userId) {
- // TODO(kroot): Add a real permission for removing users
- enforceSystemOrRoot("Only the system can remove users");
-
- if (userId == 0 || !sUserManager.exists(userId)) {
- return false;
+ /** Called by UserManagerService */
+ void cleanUpUserLILPw(int userHandle) {
+ // Disable all the packages for the user first
+ Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
+ for (Entry<String, PackageSetting> entry : entries) {
+ entry.getValue().removeUser(userHandle);
}
-
- cleanUpUser(userId);
-
- if (sUserManager.removeUser(userId)) {
- // Let other services shutdown any activity
- Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userId);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ if (mDirtyUsers.remove(userHandle));
+ mSettings.removeUserLPr(userHandle);
+ if (mInstaller != null) {
+ // Technically, we shouldn't be doing this with the package lock
+ // held. However, this is very rare, and there is already so much
+ // other disk I/O going on, that we'll let it slide for now.
+ mInstaller.removeUserDataDirs(userHandle);
}
- sUserManager.removePackageFolders(userId);
- return true;
}
- private void cleanUpUser(int userId) {
- // Disable all the packages for the user first
- synchronized (mPackages) {
- Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
- for (Entry<String, PackageSetting> entry : entries) {
- entry.getValue().removeUser(userId);
+ /** Called by UserManagerService */
+ void createNewUserLILPw(int userHandle, File path) {
+ if (mInstaller != null) {
+ path.mkdir();
+ FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+ | FileUtils.S_IXOTH, -1, -1);
+ for (PackageSetting ps : mSettings.mPackages.values()) {
+ // Only system apps are initially installed.
+ ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
+ // Need to create a data directory for all apps under this user.
+ mInstaller.createUserData(ps.name,
+ UserHandle.getUid(userHandle, ps.appId), userHandle);
}
- if (mDirtyUsers.remove(userId));
- mSettings.removeUserLPr(userId);
+ mSettings.writePackageRestrictionsLPr(userHandle);
}
}
@@ -9472,24 +9833,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- public List<UserInfo> getUsers() {
- enforceSystemOrRoot("Only the system can query users");
- return sUserManager.getUsers();
- }
-
- @Override
- public UserInfo getUser(int userId) {
- enforceSystemOrRoot("Only the system can remove users");
- return sUserManager.getUser(userId);
- }
-
- @Override
- public void updateUserName(int userId, String name) {
- enforceSystemOrRoot("Only the system can rename users");
- sUserManager.updateUserName(userId, name);
- }
-
- @Override
public void setPermissionEnforced(String permission, boolean enforced) {
mContext.enforceCallingOrSelfPermission(GRANT_REVOKE_PERMISSIONS, null);
if (READ_EXTERNAL_STORAGE.equals(permission)) {
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index 56f2166..6d31f0e 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -20,11 +20,13 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import android.content.pm.PackageUserState;
+import android.content.pm.UserInfo;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import java.io.File;
import java.util.HashSet;
+import java.util.List;
/**
* Settings base class for pending and resolved classes.
@@ -62,19 +64,11 @@ class PackageSettingBase extends GrantedPermissions {
boolean permissionsFixed;
boolean haveGids;
+ private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
+
// Whether this package is currently stopped, thus can not be
// started until explicitly launched by the user.
- private SparseArray<Boolean> stopped = new SparseArray<Boolean>();
-
- // Set to true if we have never launched this app.
- private SparseArray<Boolean> notLaunched = new SparseArray<Boolean>();
-
- /* Explicitly disabled components */
- private SparseArray<HashSet<String>> disabledComponents = new SparseArray<HashSet<String>>();
- /* Explicitly enabled components */
- private SparseArray<HashSet<String>> enabledComponents = new SparseArray<HashSet<String>>();
- /* Enabled state */
- private SparseIntArray enabled = new SparseIntArray();
+ private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
int installStatus = PKG_INSTALL_COMPLETE;
@@ -115,12 +109,11 @@ class PackageSettingBase extends GrantedPermissions {
permissionsFixed = base.permissionsFixed;
haveGids = base.haveGids;
- notLaunched = base.notLaunched;
-
- disabledComponents = (SparseArray<HashSet<String>>) base.disabledComponents.clone();
- enabledComponents = (SparseArray<HashSet<String>>) base.enabledComponents.clone();
- enabled = (SparseIntArray) base.enabled.clone();
- stopped = (SparseArray<Boolean>) base.stopped.clone();
+ userState.clear();
+ for (int i=0; i<base.userState.size(); i++) {
+ userState.put(base.userState.keyAt(i),
+ new PackageUserState(base.userState.valueAt(i)));
+ }
installStatus = base.installStatus;
origPackage = base.origPackage;
@@ -171,103 +164,174 @@ class PackageSettingBase extends GrantedPermissions {
signatures = base.signatures;
permissionsFixed = base.permissionsFixed;
haveGids = base.haveGids;
- stopped = base.stopped;
- notLaunched = base.notLaunched;
- disabledComponents = base.disabledComponents;
- enabledComponents = base.enabledComponents;
- enabled = base.enabled;
+ userState.clear();
+ for (int i=0; i<base.userState.size(); i++) {
+ userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
+ }
installStatus = base.installStatus;
}
+ private PackageUserState modifyUserState(int userId) {
+ PackageUserState state = userState.get(userId);
+ if (state == null) {
+ state = new PackageUserState();
+ userState.put(userId, state);
+ }
+ return state;
+ }
+
+ public PackageUserState readUserState(int userId) {
+ PackageUserState state = userState.get(userId);
+ return state != null ? state : DEFAULT_USER_STATE;
+ }
+
void setEnabled(int state, int userId) {
- enabled.put(userId, state);
+ modifyUserState(userId).enabled = state;
}
int getEnabled(int userId) {
- return enabled.get(userId, COMPONENT_ENABLED_STATE_DEFAULT);
+ return readUserState(userId).enabled;
+ }
+
+ void setInstalled(boolean inst, int userId) {
+ modifyUserState(userId).installed = inst;
+ }
+
+ boolean getInstalled(int userId) {
+ return readUserState(userId).installed;
+ }
+
+ boolean isAnyInstalled(int[] users) {
+ for (int user: users) {
+ if (readUserState(user).installed) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int[] getInstalledUsers(int[] users) {
+ int num = 0;
+ for (int user : users) {
+ if (getInstalled(user)) {
+ num++;
+ }
+ }
+ int[] res = new int[num];
+ num = 0;
+ for (int user : users) {
+ if (getInstalled(user)) {
+ res[num] = user;
+ num++;
+ }
+ }
+ return res;
}
boolean getStopped(int userId) {
- return stopped.get(userId, false);
+ return readUserState(userId).stopped;
}
void setStopped(boolean stop, int userId) {
- stopped.put(userId, stop);
+ modifyUserState(userId).stopped = stop;
}
boolean getNotLaunched(int userId) {
- return notLaunched.get(userId, false);
+ return readUserState(userId).notLaunched;
}
void setNotLaunched(boolean stop, int userId) {
- notLaunched.put(userId, stop);
+ modifyUserState(userId).notLaunched = stop;
+ }
+
+ void setUserState(int userId, int enabled, boolean installed, boolean stopped,
+ boolean notLaunched, HashSet<String> enabledComponents,
+ HashSet<String> disabledComponents) {
+ PackageUserState state = modifyUserState(userId);
+ state.enabled = enabled;
+ state.installed = installed;
+ state.stopped = stopped;
+ state.notLaunched = notLaunched;
+ state.enabledComponents = enabledComponents;
+ state.disabledComponents = disabledComponents;
}
HashSet<String> getEnabledComponents(int userId) {
- return getComponentHashSet(enabledComponents, userId);
+ return readUserState(userId).enabledComponents;
}
HashSet<String> getDisabledComponents(int userId) {
- return getComponentHashSet(disabledComponents, userId);
+ return readUserState(userId).disabledComponents;
}
void setEnabledComponents(HashSet<String> components, int userId) {
- enabledComponents.put(userId, components);
+ modifyUserState(userId).enabledComponents = components;
}
void setDisabledComponents(HashSet<String> components, int userId) {
- disabledComponents.put(userId, components);
+ modifyUserState(userId).disabledComponents = components;
+ }
+
+ void setEnabledComponentsCopy(HashSet<String> components, int userId) {
+ modifyUserState(userId).enabledComponents = components != null
+ ? new HashSet<String>(components) : null;
}
- private HashSet<String> getComponentHashSet(SparseArray<HashSet<String>> setArray, int userId) {
- HashSet<String> set = setArray.get(userId);
- if (set == null) {
- set = new HashSet<String>(1);
- setArray.put(userId, set);
+ void setDisabledComponentsCopy(HashSet<String> components, int userId) {
+ modifyUserState(userId).disabledComponents = components != null
+ ? new HashSet<String>(components) : null;
+ }
+
+ PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
+ PackageUserState state = modifyUserState(userId);
+ if (disabled && state.disabledComponents == null) {
+ state.disabledComponents = new HashSet<String>(1);
+ }
+ if (enabled && state.enabledComponents == null) {
+ state.enabledComponents = new HashSet<String>(1);
}
- return set;
+ return state;
}
void addDisabledComponent(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- disabled.add(componentClassName);
+ modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
}
void addEnabledComponent(String componentClassName, int userId) {
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- enabled.add(componentClassName);
+ modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
}
boolean enableComponentLPw(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- boolean changed = disabled.remove(componentClassName);
- changed |= enabled.add(componentClassName);
+ PackageUserState state = modifyUserStateComponents(userId, false, true);
+ boolean changed = state.disabledComponents != null
+ ? state.disabledComponents.remove(componentClassName) : false;
+ changed |= state.enabledComponents.add(componentClassName);
return changed;
}
boolean disableComponentLPw(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- boolean changed = enabled.remove(componentClassName);
- changed |= disabled.add(componentClassName);
+ PackageUserState state = modifyUserStateComponents(userId, true, false);
+ boolean changed = state.enabledComponents != null
+ ? state.enabledComponents.remove(componentClassName) : false;
+ changed |= state.disabledComponents.add(componentClassName);
return changed;
}
boolean restoreComponentLPw(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- boolean changed = enabled.remove(componentClassName);
- changed |= disabled.remove(componentClassName);
+ PackageUserState state = modifyUserStateComponents(userId, true, true);
+ boolean changed = state.disabledComponents != null
+ ? state.disabledComponents.remove(componentClassName) : false;
+ changed |= state.enabledComponents != null
+ ? state.enabledComponents.remove(componentClassName) : false;
return changed;
}
int getCurrentEnabledStateLPr(String componentName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- if (enabled.contains(componentName)) {
+ PackageUserState state = readUserState(userId);
+ if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
return COMPONENT_ENABLED_STATE_ENABLED;
- } else if (disabled.contains(componentName)) {
+ } else if (state.disabledComponents != null
+ && state.disabledComponents.contains(componentName)) {
return COMPONENT_ENABLED_STATE_DISABLED;
} else {
return COMPONENT_ENABLED_STATE_DEFAULT;
@@ -275,11 +339,6 @@ class PackageSettingBase extends GrantedPermissions {
}
void removeUser(int userId) {
- enabled.delete(userId);
- stopped.delete(userId);
- enabledComponents.delete(userId);
- disabledComponents.delete(userId);
- notLaunched.delete(userId);
+ userState.delete(userId);
}
-
}
diff --git a/services/java/com/android/server/pm/PackageVerificationState.java b/services/java/com/android/server/pm/PackageVerificationState.java
index e5b89c1..3214e88 100644
--- a/services/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/java/com/android/server/pm/PackageVerificationState.java
@@ -43,6 +43,8 @@ class PackageVerificationState {
private boolean mRequiredVerificationPassed;
+ private boolean mExtendedTimeout;
+
/**
* Create a new package verification state where {@code requiredVerifierUid}
* is the user ID for the package that must reply affirmative before things
@@ -55,6 +57,7 @@ class PackageVerificationState {
mRequiredVerifierUid = requiredVerifierUid;
mArgs = args;
mSufficientVerifierUids = new SparseBooleanArray();
+ mExtendedTimeout = false;
}
public InstallArgs getInstallArgs() {
@@ -146,4 +149,22 @@ class PackageVerificationState {
return true;
}
+
+ /**
+ * Extend the timeout for this Package to be verified.
+ */
+ public void extendTimeout() {
+ if (!mExtendedTimeout) {
+ mExtendedTimeout = true;
+ }
+ }
+
+ /**
+ * Returns whether the timeout was extended for verification.
+ *
+ * @return {@code true} if a timeout was already extended.
+ */
+ public boolean timeoutExtended() {
+ return mExtendedTimeout;
+ }
}
diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java
index b100eb1..5539e84 100644
--- a/services/java/com/android/server/pm/PreferredActivity.java
+++ b/services/java/com/android/server/pm/PreferredActivity.java
@@ -33,22 +33,38 @@ class PreferredActivity extends IntentFilter implements PreferredComponent.Callb
private static final String TAG = "PreferredActivity";
private static final boolean DEBUG_FILTERS = false;
+ static final String ATTR_USER_ID = "userId";
final PreferredComponent mPref;
+ final int mUserId;
PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
+ this(filter, match, set, activity, 0);
+ }
+
+ PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity,
+ int userId) {
super(filter);
+ mUserId = userId;
mPref = new PreferredComponent(this, match, set, activity);
}
PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String userIdString = parser.getAttributeValue(null, ATTR_USER_ID);
+ if (userIdString != null && userIdString.length() > 0) {
+ mUserId = Integer.parseInt(userIdString);
+ } else {
+ // Old format with no userId specified - assume primary user
+ mUserId = 0;
+ }
mPref = new PreferredComponent(this, parser);
}
public void writeToXml(XmlSerializer serializer) throws IOException {
+ serializer.attribute(null, ATTR_USER_ID, Integer.toString(mUserId));
mPref.writeToXml(serializer);
serializer.startTag(null, "filter");
- super.writeToXml(serializer);
+ super.writeToXml(serializer);
serializer.endTag(null, "filter");
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 120b650..68b594a 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -34,21 +34,24 @@ import org.xmlpull.v1.XmlSerializer;
import android.app.AppGlobals;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
+import android.content.pm.PackageCleanItem;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
+import android.content.pm.PackageUserState;
import android.content.pm.VerifierDeviceIdentity;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -79,6 +82,7 @@ final class Settings {
private static final String TAG = "PackageSettings";
private static final boolean DEBUG_STOPPED = false;
+ private static final boolean DEBUG_MU = false;
private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage";
private static final String ATTR_ENFORCEMENT = "enforcement";
@@ -90,9 +94,12 @@ final class Settings {
private static final String TAG_PACKAGE = "pkg";
private static final String ATTR_NAME = "name";
+ private static final String ATTR_USER = "user";
+ private static final String ATTR_CODE = "code";
private static final String ATTR_NOT_LAUNCHED = "nl";
private static final String ATTR_ENABLED = "enabled";
private static final String ATTR_STOPPED = "stopped";
+ private static final String ATTR_INSTALLED = "inst";
private final File mSettingsFilename;
private final File mBackupSettingsFilename;
@@ -121,6 +128,10 @@ final class Settings {
final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
new IntentResolver<PreferredActivity, PreferredActivity>() {
@Override
+ protected PreferredActivity[] newArray(int size) {
+ return new PreferredActivity[size];
+ }
+ @Override
protected String packageForFilter(PreferredActivity filter) {
return filter.mPref.mComponent.getPackageName();
}
@@ -150,7 +161,8 @@ final class Settings {
// Packages that have been uninstalled and still need their external
// storage data deleted.
- final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+ final SparseArray<ArrayList<PackageCleanItem>> mPackagesToBeCleaned
+ = new SparseArray<ArrayList<PackageCleanItem>>();
// Packages that have been renamed since they were first installed.
// Keys are the new names of the packages, values are the original
@@ -169,12 +181,15 @@ final class Settings {
*/
private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>();
+ private final Context mContext;
+
private final File mSystemDir;
- Settings() {
- this(Environment.getDataDirectory());
+ Settings(Context context) {
+ this(context, Environment.getDataDirectory());
}
- Settings(File dataDir) {
+ Settings(Context context, File dataDir) {
+ mContext = context;
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
@@ -191,10 +206,11 @@ final class Settings {
PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
+ String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) {
final String name = pkg.packageName;
PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
- resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
+ resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags,
+ user, add);
return p;
}
@@ -355,7 +371,8 @@ final class Settings {
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
+ String nativeLibraryPathString, int vc, int pkgFlags,
+ UserHandle installUser, boolean add) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (!p.codePath.equals(codePath)) {
@@ -398,11 +415,6 @@ final class Settings {
}
}
if (p == null) {
- // Create a new PackageSettings entry. this can end up here because
- // of code path mismatch or user id mismatch of an updated system partition
- if (!create) {
- return null;
- }
if (origPackage != null) {
// We are consuming the data from an existing package.
p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
@@ -436,8 +448,20 @@ final class Settings {
List<UserInfo> users = getAllUsers();
if (users != null) {
for (UserInfo user : users) {
- p.setStopped(true, user.id);
- p.setNotLaunched(true, user.id);
+ // By default we consider this app to be installed
+ // for the user if no user has been specified (which
+ // means to leave it at its original value, and the
+ // original default value is true), or we are being
+ // asked to install for all users, or this is the
+ // user we are installing for.
+ final boolean installed = installUser == null
+ || installUser.getIdentifier() == UserHandle.USER_ALL
+ || installUser.getIdentifier() == user.id;
+ p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
+ installed,
+ true, // stopped,
+ true, // notLaunched
+ null, null);
writePackageRestrictionsLPr(user.id);
}
}
@@ -463,12 +487,10 @@ final class Settings {
if (users != null) {
for (UserInfo user : users) {
int userId = user.id;
- p.setDisabledComponents(
- new HashSet<String>(dis.getDisabledComponents(userId)),
- userId);
- p.setEnabledComponents(
- new HashSet<String>(dis.getEnabledComponents(userId)),
- userId);
+ p.setDisabledComponentsCopy(
+ dis.getDisabledComponents(userId), userId);
+ p.setEnabledComponentsCopy(
+ dis.getEnabledComponents(userId), userId);
}
}
// Add new setting to list of user ids
@@ -489,6 +511,25 @@ final class Settings {
// user preferences
addPackageSettingLPw(p, name, sharedUser);
}
+ } else {
+ if (installUser != null) {
+ // The caller has explicitly specified the user they want this
+ // package installed for, and the package already exists.
+ // Make sure it conforms to the new request.
+ List<UserInfo> users = getAllUsers();
+ if (users != null) {
+ for (UserInfo user : users) {
+ if (installUser.getIdentifier() == UserHandle.USER_ALL
+ || installUser.getIdentifier() == user.id) {
+ boolean installed = p.getInstalled(user.id);
+ if (!installed) {
+ p.setInstalled(true, user.id);
+ writePackageRestrictionsLPr(user.id);
+ }
+ }
+ }
+ }
+ }
}
return p;
}
@@ -735,6 +776,9 @@ final class Settings {
}
void readPackageRestrictionsLPr(int userId) {
+ if (DEBUG_MU) {
+ Log.i(TAG, "Reading package restrictions for user=" + userId);
+ }
FileInputStream str = null;
File userPackagesStateFile = getUserPackagesStateFile(userId);
File backupFile = getUserPackagesStateBackupFile(userId);
@@ -766,10 +810,14 @@ final class Settings {
+ "assuming all started");
// At first boot, make sure no packages are stopped.
// We usually want to have third party apps initialize
- // in the stopped state, but not at first boot.
+ // in the stopped state, but not at first boot. Also
+ // consider all applications to be installed.
for (PackageSetting pkg : mPackages.values()) {
- pkg.setStopped(false, userId);
- pkg.setNotLaunched(false, userId);
+ pkg.setUserState(userId, COMPONENT_ENABLED_STATE_DEFAULT,
+ true, // installed
+ false, // stopped
+ false, // notLaunched
+ null, null);
}
return;
}
@@ -811,17 +859,21 @@ final class Settings {
XmlUtils.skipCurrentTag(parser);
continue;
}
- String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
- int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT
- : Integer.parseInt(enabledStr);
- ps.setEnabled(enabled, userId);
- String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
- boolean stopped = stoppedStr == null ? false : Boolean.parseBoolean(stoppedStr);
- ps.setStopped(stopped, userId);
- String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
- boolean notLaunched = stoppedStr == null ? false
- : Boolean.parseBoolean(notLaunchedStr);
- ps.setNotLaunched(notLaunched, userId);
+ final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
+ final int enabled = enabledStr == null
+ ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr);
+ final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED);
+ final boolean installed = installedStr == null
+ ? true : Boolean.parseBoolean(installedStr);
+ final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
+ final boolean stopped = stoppedStr == null
+ ? false : Boolean.parseBoolean(stoppedStr);
+ final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
+ final boolean notLaunched = stoppedStr == null
+ ? false : Boolean.parseBoolean(notLaunchedStr);
+
+ HashSet<String> enabledComponents = null;
+ HashSet<String> disabledComponents = null;
int packageDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -833,13 +885,14 @@ final class Settings {
}
tagName = parser.getName();
if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
- HashSet<String> components = readComponentsLPr(parser);
- ps.setEnabledComponents(components, userId);
+ enabledComponents = readComponentsLPr(parser);
} else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
- HashSet<String> components = readComponentsLPr(parser);
- ps.setDisabledComponents(components, userId);
+ disabledComponents = readComponentsLPr(parser);
}
}
+
+ ps.setUserState(userId, enabled, installed, stopped, notLaunched,
+ enabledComponents, disabledComponents);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -864,7 +917,7 @@ final class Settings {
private HashSet<String> readComponentsLPr(XmlPullParser parser)
throws IOException, XmlPullParserException {
- HashSet<String> components = new HashSet<String>();
+ HashSet<String> components = null;
int type;
int outerDepth = parser.getDepth();
String tagName;
@@ -879,6 +932,9 @@ final class Settings {
if (tagName.equals(TAG_ITEM)) {
String componentName = parser.getAttributeValue(null, ATTR_NAME);
if (componentName != null) {
+ if (components == null) {
+ components = new HashSet<String>();
+ }
components.add(componentName);
}
}
@@ -887,6 +943,9 @@ final class Settings {
}
void writePackageRestrictionsLPr(int userId) {
+ if (DEBUG_MU) {
+ Log.i(TAG, "Writing package restrictions for user=" + userId);
+ }
// Keep the old stopped packages around until we know the new ones have
// been successfully written.
File userPackagesStateFile = getUserPackagesStateFile(userId);
@@ -921,40 +980,44 @@ final class Settings {
serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
for (final PackageSetting pkg : mPackages.values()) {
- if (pkg.getStopped(userId)
- || pkg.getNotLaunched(userId)
- || pkg.getEnabled(userId) != COMPONENT_ENABLED_STATE_DEFAULT
- || pkg.getEnabledComponents(userId).size() > 0
- || pkg.getDisabledComponents(userId).size() > 0) {
+ PackageUserState ustate = pkg.readUserState(userId);
+ if (ustate.stopped || ustate.notLaunched || !ustate.installed
+ || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
+ || (ustate.enabledComponents != null
+ && ustate.enabledComponents.size() > 0)
+ || (ustate.disabledComponents != null
+ && ustate.disabledComponents.size() > 0)) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
- boolean stopped = pkg.getStopped(userId);
- boolean notLaunched = pkg.getNotLaunched(userId);
- int enabled = pkg.getEnabled(userId);
- HashSet<String> enabledComponents = pkg.getEnabledComponents(userId);
- HashSet<String> disabledComponents = pkg.getDisabledComponents(userId);
+ if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
- if (stopped) {
+ if (!ustate.installed) {
+ serializer.attribute(null, ATTR_INSTALLED, "false");
+ }
+ if (ustate.stopped) {
serializer.attribute(null, ATTR_STOPPED, "true");
}
- if (notLaunched) {
+ if (ustate.notLaunched) {
serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
}
- if (enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
- serializer.attribute(null, ATTR_ENABLED, Integer.toString(enabled));
+ if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
+ serializer.attribute(null, ATTR_ENABLED,
+ Integer.toString(ustate.enabled));
}
- if (enabledComponents.size() > 0) {
+ if (ustate.enabledComponents != null
+ && ustate.enabledComponents.size() > 0) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
- for (final String name : enabledComponents) {
+ for (final String name : ustate.enabledComponents) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME, name);
serializer.endTag(null, TAG_ITEM);
}
serializer.endTag(null, TAG_ENABLED_COMPONENTS);
}
- if (disabledComponents.size() > 0) {
+ if (ustate.disabledComponents != null
+ && ustate.disabledComponents.size() > 0) {
serializer.startTag(null, TAG_DISABLED_COMPONENTS);
- for (final String name : disabledComponents) {
+ for (final String name : ustate.disabledComponents) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME, name);
serializer.endTag(null, TAG_ITEM);
@@ -1194,9 +1257,17 @@ final class Settings {
if (mPackagesToBeCleaned.size() > 0) {
for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
- serializer.startTag(null, "cleaning-package");
- serializer.attribute(null, ATTR_NAME, mPackagesToBeCleaned.get(i));
- serializer.endTag(null, "cleaning-package");
+ final int userId = mPackagesToBeCleaned.keyAt(i);
+ final String userStr = Integer.toString(userId);
+ final ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.valueAt(i);
+ for (int j=0; j<pkgs.size(); j++) {
+ serializer.startTag(null, "cleaning-package");
+ PackageCleanItem item = pkgs.get(i);
+ serializer.attribute(null, ATTR_NAME, item.packageName);
+ serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false");
+ serializer.attribute(null, ATTR_USER, userStr);
+ serializer.endTag(null, "cleaning-package");
+ }
}
}
@@ -1452,6 +1523,17 @@ final class Settings {
return ret;
}
+ void addPackageToCleanLPw(int userId, PackageCleanItem pkg) {
+ ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.get(userId);
+ if (pkgs == null) {
+ pkgs = new ArrayList<PackageCleanItem>();
+ mPackagesToBeCleaned.put(userId, pkgs);
+ }
+ if (!pkgs.contains(pkg)) {
+ pkgs.add(pkg);
+ }
+ }
+
boolean readLPw(List<UserInfo> users) {
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
@@ -1529,8 +1611,21 @@ final class Settings {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
String name = parser.getAttributeValue(null, ATTR_NAME);
+ String userStr = parser.getAttributeValue(null, ATTR_USER);
+ String codeStr = parser.getAttributeValue(null, ATTR_CODE);
if (name != null) {
- mPackagesToBeCleaned.add(name);
+ int user = 0;
+ boolean andCode = true;
+ try {
+ if (userStr != null) {
+ user = Integer.parseInt(userStr);
+ }
+ } catch (NumberFormatException e) {
+ }
+ if (codeStr != null) {
+ andCode = Boolean.parseBoolean(codeStr);
+ }
+ addPackageToCleanLPw(user, new PackageCleanItem(name, andCode));
}
} else if (tagName.equals("renamed-package")) {
String nname = parser.getAttributeValue(null, "new");
@@ -1580,7 +1675,24 @@ final class Settings {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+ }
+ if (mBackupStoppedPackagesFilename.exists()
+ || mStoppedPackagesFilename.exists()) {
+ // Read old file
+ readStoppedLPw();
+ mBackupStoppedPackagesFilename.delete();
+ mStoppedPackagesFilename.delete();
+ // Migrate to new file format
+ writePackageRestrictionsLPr(0);
+ } else {
+ if (users == null) {
+ readPackageRestrictionsLPr(0);
+ } else {
+ for (UserInfo user : users) {
+ readPackageRestrictionsLPr(user.id);
+ }
+ }
}
final int N = mPendingPackages.size();
@@ -1590,7 +1702,8 @@ final class Settings {
if (idObj != null && idObj instanceof SharedUserSetting) {
PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
(SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
- pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
+ pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags,
+ UserHandle.ALL, true);
if (p == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unable to create application package for " + pp.name);
@@ -1624,23 +1737,6 @@ final class Settings {
}
}
- if (mBackupStoppedPackagesFilename.exists()
- || mStoppedPackagesFilename.exists()) {
- // Read old file
- readStoppedLPw();
- mBackupStoppedPackagesFilename.delete();
- mStoppedPackagesFilename.delete();
- // Migrate to new file format
- writePackageRestrictionsLPr(0);
- } else {
- if (users == null) {
- readPackageRestrictionsLPr(0);
- } else {
- for (UserInfo user : users) {
- readPackageRestrictionsLPr(user.id);
- }
- }
- }
mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "
+ mSharedUsers.size() + " shared uids\n");
@@ -2276,6 +2372,10 @@ final class Settings {
return ps;
}
+ private String compToString(HashSet<String> cmp) {
+ return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
+ }
+
boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
return true;
@@ -2286,24 +2386,26 @@ final class Settings {
Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
+ componentInfo.packageName + " componentName = " + componentInfo.name);
Log.v(PackageManagerService.TAG, "enabledComponents: "
- + Arrays.toString(packageSettings.getEnabledComponents(userId).toArray()));
+ + compToString(packageSettings.getEnabledComponents(userId)));
Log.v(PackageManagerService.TAG, "disabledComponents: "
- + Arrays.toString(packageSettings.getDisabledComponents(userId).toArray()));
+ + compToString(packageSettings.getDisabledComponents(userId)));
}
if (packageSettings == null) {
return false;
}
- final int enabled = packageSettings.getEnabled(userId);
- if (enabled == COMPONENT_ENABLED_STATE_DISABLED
- || enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
+ PackageUserState ustate = packageSettings.readUserState(userId);
+ if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
+ || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
|| (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
- && enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+ && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
return false;
}
- if (packageSettings.getEnabledComponents(userId).contains(componentInfo.name)) {
+ if (ustate.enabledComponents != null
+ && ustate.enabledComponents.contains(componentInfo.name)) {
return true;
}
- if (packageSettings.getDisabledComponents(userId).contains(componentInfo.name)) {
+ if (ustate.disabledComponents != null
+ && ustate.disabledComponents.contains(componentInfo.name)) {
return false;
}
return componentInfo.enabled;
@@ -2337,7 +2439,7 @@ final class Settings {
boolean setPackageStoppedStateLPw(String packageName, boolean stopped,
boolean allowedByPermission, int uid, int userId) {
- int appId = UserId.getAppId(uid);
+ int appId = UserHandle.getAppId(uid);
final PackageSetting pkgSetting = mPackages.get(packageName);
if (pkgSetting == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -2362,7 +2464,7 @@ final class Settings {
if (pkgSetting.installerPackageName != null) {
PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
pkgSetting.name, null,
- pkgSetting.installerPackageName, null, userId);
+ pkgSetting.installerPackageName, null, new int[] {userId});
}
pkgSetting.setNotLaunched(false, userId);
}
@@ -2374,9 +2476,7 @@ final class Settings {
private List<UserInfo> getAllUsers() {
long id = Binder.clearCallingIdentity();
try {
- return AppGlobals.getPackageManager().getUsers();
- } catch (RemoteException re) {
- // Local to system process, shouldn't happen
+ return UserManagerService.getInstance().getUsers();
} catch (NullPointerException npe) {
// packagemanager not yet initialized
} finally {
@@ -2413,7 +2513,6 @@ final class Settings {
ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION",
ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE",
ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP",
- ApplicationInfo.FLAG_STOPPED, "STOPPED",
ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK",
ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
};
@@ -2523,22 +2622,28 @@ final class Settings {
pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
pw.print(" haveGids="); pw.println(ps.haveGids);
pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
- pw.print(" installStatus="); pw.print(ps.installStatus);
+ pw.print(" installStatus="); pw.println(ps.installStatus);
for (UserInfo user : users) {
- pw.print(" User "); pw.print(user.id); pw.print(": ");
+ pw.print(" User "); pw.print(user.id); pw.print(": ");
+ pw.print(" installed=");
+ pw.print(ps.getInstalled(user.id));
pw.print(" stopped=");
pw.print(ps.getStopped(user.id));
+ pw.print(" notLaunched=");
+ pw.print(ps.getNotLaunched(user.id));
pw.print(" enabled=");
pw.println(ps.getEnabled(user.id));
- if (ps.getDisabledComponents(user.id).size() > 0) {
- pw.println(" disabledComponents:");
- for (String s : ps.getDisabledComponents(user.id)) {
+ HashSet<String> cmp = ps.getDisabledComponents(user.id);
+ if (cmp != null && cmp.size() > 0) {
+ pw.println(" disabledComponents:");
+ for (String s : cmp) {
pw.print(" "); pw.println(s);
}
}
- if (ps.getEnabledComponents(user.id).size() > 0) {
- pw.println(" enabledComponents:");
- for (String s : ps.getEnabledComponents(user.id)) {
+ cmp = ps.getEnabledComponents(user.id);
+ if (cmp != null && cmp.size() > 0) {
+ pw.println(" enabledComponents:");
+ for (String s : cmp) {
pw.print(" "); pw.println(s);
}
}
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
deleted file mode 100644
index 4e9e666..0000000
--- a/services/java/com/android/server/pm/UserManager.java
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Environment;
-import android.os.FileUtils;
-import android.os.SystemClock;
-import android.os.UserId;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.Xml;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-public class UserManager {
- private static final String TAG_NAME = "name";
-
- private static final String ATTR_FLAGS = "flags";
-
- private static final String ATTR_ID = "id";
-
- private static final String TAG_USERS = "users";
-
- private static final String TAG_USER = "user";
-
- private static final String LOG_TAG = "UserManager";
-
- private static final String USER_INFO_DIR = "system" + File.separator + "users";
- private static final String USER_LIST_FILENAME = "userlist.xml";
-
- private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
-
- private final File mUsersDir;
- private final File mUserListFile;
- private int[] mUserIds;
-
- private Installer mInstaller;
- private File mBaseUserPath;
-
- /**
- * Available for testing purposes.
- */
- UserManager(File dataDir, File baseUserPath) {
- mUsersDir = new File(dataDir, USER_INFO_DIR);
- mUsersDir.mkdirs();
- // Make zeroth user directory, for services to migrate their files to that location
- File userZeroDir = new File(mUsersDir, "0");
- userZeroDir.mkdirs();
- mBaseUserPath = baseUserPath;
- FileUtils.setPermissions(mUsersDir.toString(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG
- |FileUtils.S_IROTH|FileUtils.S_IXOTH,
- -1, -1);
- mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
- readUserList();
- }
-
- public UserManager(Installer installer, File baseUserPath) {
- this(Environment.getDataDirectory(), baseUserPath);
- mInstaller = installer;
- }
-
- public List<UserInfo> getUsers() {
- synchronized (mUsers) {
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
- for (int i = 0; i < mUsers.size(); i++) {
- users.add(mUsers.valueAt(i));
- }
- return users;
- }
- }
-
- public UserInfo getUser(int userId) {
- synchronized (mUsers) {
- UserInfo info = mUsers.get(userId);
- return info;
- }
- }
-
- public boolean exists(int userId) {
- synchronized (mUsers) {
- return ArrayUtils.contains(mUserIds, userId);
- }
- }
-
- public void updateUserName(int userId, String name) {
- synchronized (mUsers) {
- UserInfo info = mUsers.get(userId);
- if (name != null && !name.equals(info.name)) {
- info.name = name;
- writeUserLocked(info);
- }
- }
- }
-
- /**
- * Returns an array of user ids. This array is cached here for quick access, so do not modify or
- * cache it elsewhere.
- * @return the array of user ids.
- */
- int[] getUserIds() {
- return mUserIds;
- }
-
- private void readUserList() {
- synchronized (mUsers) {
- readUserListLocked();
- }
- }
-
- private void readUserListLocked() {
- if (!mUserListFile.exists()) {
- fallbackToSingleUserLocked();
- return;
- }
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(mUserListFile);
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
-
- if (type != XmlPullParser.START_TAG) {
- Slog.e(LOG_TAG, "Unable to read user list");
- fallbackToSingleUserLocked();
- return;
- }
-
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
- String id = parser.getAttributeValue(null, ATTR_ID);
- UserInfo user = readUser(Integer.parseInt(id));
- if (user != null) {
- mUsers.put(user.id, user);
- }
- }
- }
- updateUserIdsLocked();
- } catch (IOException ioe) {
- fallbackToSingleUserLocked();
- } catch (XmlPullParserException pe) {
- fallbackToSingleUserLocked();
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- }
- }
- }
- }
-
- private void fallbackToSingleUserLocked() {
- // Create the primary user
- UserInfo primary = new UserInfo(0, "Primary",
- UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
- mUsers.put(0, primary);
- updateUserIdsLocked();
-
- writeUserListLocked();
- writeUserLocked(primary);
- }
-
- /*
- * Writes the user file in this format:
- *
- * <user flags="20039023" id="0">
- * <name>Primary</name>
- * </user>
- */
- private void writeUserLocked(UserInfo userInfo) {
- FileOutputStream fos = null;
- try {
- final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
- fos = new FileOutputStream(mUserFile);
- final BufferedOutputStream bos = new BufferedOutputStream(fos);
-
- // XmlSerializer serializer = XmlUtils.serializerInstance();
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(bos, "utf-8");
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
- serializer.startTag(null, TAG_USER);
- serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
- serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
-
- serializer.startTag(null, TAG_NAME);
- serializer.text(userInfo.name);
- serializer.endTag(null, TAG_NAME);
-
- serializer.endTag(null, TAG_USER);
-
- serializer.endDocument();
- } catch (IOException ioe) {
- Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException ioe) {
- }
- }
- }
- }
-
- /*
- * Writes the user list file in this format:
- *
- * <users>
- * <user id="0"></user>
- * <user id="2"></user>
- * </users>
- */
- private void writeUserListLocked() {
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(mUserListFile);
- final BufferedOutputStream bos = new BufferedOutputStream(fos);
-
- // XmlSerializer serializer = XmlUtils.serializerInstance();
- final XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(bos, "utf-8");
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
- serializer.startTag(null, TAG_USERS);
-
- for (int i = 0; i < mUsers.size(); i++) {
- UserInfo user = mUsers.valueAt(i);
- serializer.startTag(null, TAG_USER);
- serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
- serializer.endTag(null, TAG_USER);
- }
-
- serializer.endTag(null, TAG_USERS);
-
- serializer.endDocument();
- } catch (IOException ioe) {
- Slog.e(LOG_TAG, "Error writing user list");
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException ioe) {
- }
- }
- }
- }
-
- private UserInfo readUser(int id) {
- int flags = 0;
- String name = null;
-
- FileInputStream fis = null;
- try {
- File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
- fis = new FileInputStream(userFile);
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
-
- if (type != XmlPullParser.START_TAG) {
- Slog.e(LOG_TAG, "Unable to read user " + id);
- return null;
- }
-
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
- String storedId = parser.getAttributeValue(null, ATTR_ID);
- if (Integer.parseInt(storedId) != id) {
- Slog.e(LOG_TAG, "User id does not match the file name");
- return null;
- }
- String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
- flags = Integer.parseInt(flagString);
-
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- }
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
- type = parser.next();
- if (type == XmlPullParser.TEXT) {
- name = parser.getText();
- }
- }
- }
-
- UserInfo userInfo = new UserInfo(id, name, flags);
- return userInfo;
-
- } catch (IOException ioe) {
- } catch (XmlPullParserException pe) {
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- }
- }
- }
- return null;
- }
-
- public UserInfo createUser(String name, int flags) {
- int userId = getNextAvailableId();
- UserInfo userInfo = new UserInfo(userId, name, flags);
- File userPath = new File(mBaseUserPath, Integer.toString(userId));
- if (!createPackageFolders(userId, userPath)) {
- return null;
- }
- synchronized (mUsers) {
- mUsers.put(userId, userInfo);
- writeUserListLocked();
- writeUserLocked(userInfo);
- updateUserIdsLocked();
- }
- return userInfo;
- }
-
- /**
- * Removes a user and all data directories created for that user. This method should be called
- * after the user's processes have been terminated.
- * @param id the user's id
- */
- public boolean removeUser(int id) {
- synchronized (mUsers) {
- return removeUserLocked(id);
- }
- }
-
- private boolean removeUserLocked(int id) {
- // Remove from the list
- UserInfo userInfo = mUsers.get(id);
- if (userInfo != null) {
- // Remove this user from the list
- mUsers.remove(id);
- // Remove user file
- File userFile = new File(mUsersDir, id + ".xml");
- userFile.delete();
- // Update the user list
- writeUserListLocked();
- updateUserIdsLocked();
- return true;
- }
- return false;
- }
-
- public void installPackageForAllUsers(String packageName, int uid) {
- for (int userId : mUserIds) {
- // Don't do it for the primary user, it will become recursive.
- if (userId == 0)
- continue;
- mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
- userId);
- }
- }
-
- public void clearUserDataForAllUsers(String packageName) {
- for (int userId : mUserIds) {
- // Don't do it for the primary user, it will become recursive.
- if (userId == 0)
- continue;
- mInstaller.clearUserData(packageName, userId);
- }
- }
-
- public void removePackageForAllUsers(String packageName) {
- for (int userId : mUserIds) {
- // Don't do it for the primary user, it will become recursive.
- if (userId == 0)
- continue;
- mInstaller.remove(packageName, userId);
- }
- }
-
- /**
- * Caches the list of user ids in an array, adjusting the array size when necessary.
- */
- private void updateUserIdsLocked() {
- if (mUserIds == null || mUserIds.length != mUsers.size()) {
- mUserIds = new int[mUsers.size()];
- }
- for (int i = 0; i < mUsers.size(); i++) {
- mUserIds[i] = mUsers.keyAt(i);
- }
- }
-
- /**
- * Returns the next available user id, filling in any holes in the ids.
- * TODO: May not be a good idea to recycle ids, in case it results in confusion
- * for data and battery stats collection, or unexpected cross-talk.
- * @return
- */
- private int getNextAvailableId() {
- int i = 0;
- while (i < Integer.MAX_VALUE) {
- if (mUsers.indexOfKey(i) < 0) {
- break;
- }
- i++;
- }
- return i;
- }
-
- private boolean createPackageFolders(int id, File userPath) {
- // mInstaller may not be available for unit-tests.
- if (mInstaller == null) return true;
-
- // Create the user path
- userPath.mkdir();
- FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
- | FileUtils.S_IXOTH, -1, -1);
-
- mInstaller.cloneUserData(0, id, false);
-
- return true;
- }
-
- boolean removePackageFolders(int id) {
- // mInstaller may not be available for unit-tests.
- if (mInstaller == null) return true;
-
- mInstaller.removeUserDataDirs(id);
- return true;
- }
-}
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
new file mode 100644
index 0000000..750aa72
--- /dev/null
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.IUserManager;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+public class UserManagerService extends IUserManager.Stub {
+
+ private static final String LOG_TAG = "UserManagerService";
+
+ private static final String TAG_NAME = "name";
+ private static final String ATTR_FLAGS = "flags";
+ private static final String ATTR_ICON_PATH = "icon";
+ private static final String ATTR_ID = "id";
+ private static final String ATTR_SERIAL_NO = "serialNumber";
+ private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
+ private static final String TAG_USERS = "users";
+ private static final String TAG_USER = "user";
+
+ private static final String USER_INFO_DIR = "system" + File.separator + "users";
+ private static final String USER_LIST_FILENAME = "userlist.xml";
+ private static final String USER_PHOTO_FILENAME = "photo.png";
+
+ private final Context mContext;
+ private final PackageManagerService mPm;
+ private final Object mInstallLock;
+ private final Object mPackagesLock;
+
+ private final File mUsersDir;
+ private final File mUserListFile;
+ private final File mBaseUserPath;
+
+ private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+
+ private int[] mUserIds;
+ private boolean mGuestEnabled;
+ private int mNextSerialNumber;
+
+ private static UserManagerService sInstance;
+
+ public static UserManagerService getInstance() {
+ synchronized (UserManagerService.class) {
+ return sInstance;
+ }
+ }
+
+ /**
+ * Available for testing purposes.
+ */
+ UserManagerService(File dataDir, File baseUserPath) {
+ this(null, null, new Object(), new Object(), dataDir, baseUserPath);
+ }
+
+ /**
+ * Called by package manager to create the service. This is closely
+ * associated with the package manager, and the given lock is the
+ * package manager's own lock.
+ */
+ UserManagerService(Context context, PackageManagerService pm,
+ Object installLock, Object packagesLock) {
+ this(context, pm, installLock, packagesLock,
+ Environment.getDataDirectory(),
+ new File(Environment.getDataDirectory(), "user"));
+ }
+
+ /**
+ * Available for testing purposes.
+ */
+ private UserManagerService(Context context, PackageManagerService pm,
+ Object installLock, Object packagesLock,
+ File dataDir, File baseUserPath) {
+ synchronized (UserManagerService.class) {
+ mContext = context;
+ mPm = pm;
+ mInstallLock = installLock;
+ mPackagesLock = packagesLock;
+ mUsersDir = new File(dataDir, USER_INFO_DIR);
+ mUsersDir.mkdirs();
+ // Make zeroth user directory, for services to migrate their files to that location
+ File userZeroDir = new File(mUsersDir, "0");
+ userZeroDir.mkdirs();
+ mBaseUserPath = baseUserPath;
+ FileUtils.setPermissions(mUsersDir.toString(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG
+ |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+ -1, -1);
+ mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+ readUserList();
+ sInstance = this;
+ }
+ }
+
+ @Override
+ public List<UserInfo> getUsers() {
+ checkManageUsersPermission("query users");
+ synchronized (mPackagesLock) {
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ for (int i = 0; i < mUsers.size(); i++) {
+ users.add(mUsers.valueAt(i));
+ }
+ return users;
+ }
+ }
+
+ @Override
+ public UserInfo getUserInfo(int userId) {
+ checkManageUsersPermission("query user");
+ synchronized (mPackagesLock) {
+ return getUserInfoLocked(userId);
+ }
+ }
+
+ /*
+ * Should be locked on mUsers before calling this.
+ */
+ private UserInfo getUserInfoLocked(int userId) {
+ return mUsers.get(userId);
+ }
+
+ public boolean exists(int userId) {
+ synchronized (mPackagesLock) {
+ return ArrayUtils.contains(mUserIds, userId);
+ }
+ }
+
+ @Override
+ public void setUserName(int userId, String name) {
+ checkManageUsersPermission("rename users");
+ synchronized (mPackagesLock) {
+ UserInfo info = mUsers.get(userId);
+ if (name != null && !name.equals(info.name)) {
+ info.name = name;
+ writeUserLocked(info);
+ }
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor setUserIcon(int userId) {
+ checkManageUsersPermission("update users");
+ synchronized (mPackagesLock) {
+ UserInfo info = mUsers.get(userId);
+ if (info == null) return null;
+ ParcelFileDescriptor fd = updateIconBitmapLocked(info);
+ if (fd != null) {
+ writeUserLocked(info);
+ }
+ return fd;
+ }
+ }
+
+ @Override
+ public void setGuestEnabled(boolean enable) {
+ checkManageUsersPermission("enable guest users");
+ synchronized (mPackagesLock) {
+ if (mGuestEnabled != enable) {
+ mGuestEnabled = enable;
+ // Erase any guest user that currently exists
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ if (user.isGuest()) {
+ if (!enable) {
+ removeUser(user.id);
+ }
+ return;
+ }
+ }
+ // No guest was found
+ if (enable) {
+ createUser("Guest", UserInfo.FLAG_GUEST);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isGuestEnabled() {
+ synchronized (mPackagesLock) {
+ return mGuestEnabled;
+ }
+ }
+
+ @Override
+ public void wipeUser(int userHandle) {
+ checkManageUsersPermission("wipe user");
+ // TODO:
+ }
+
+ /**
+ * Enforces that only the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
+ * permission can make certain calls to the UserManager.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller is not system or root
+ */
+ private static final void checkManageUsersPermission(String message) {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID && uid != 0
+ && ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MANAGE_USERS,
+ uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need MANAGE_USERS permission to: " + message);
+ }
+ }
+
+ private ParcelFileDescriptor updateIconBitmapLocked(UserInfo info) {
+ try {
+ File dir = new File(mUsersDir, Integer.toString(info.id));
+ File file = new File(dir, USER_PHOTO_FILENAME);
+ if (!dir.exists()) {
+ dir.mkdir();
+ FileUtils.setPermissions(
+ dir.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
+ MODE_CREATE|MODE_READ_WRITE);
+ info.iconPath = file.getAbsolutePath();
+ return fd;
+ } catch (FileNotFoundException e) {
+ Slog.w(LOG_TAG, "Error setting photo for user ", e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns an array of user ids. This array is cached here for quick access, so do not modify or
+ * cache it elsewhere.
+ * @return the array of user ids.
+ */
+ int[] getUserIds() {
+ synchronized (mPackagesLock) {
+ return mUserIds;
+ }
+ }
+
+ int[] getUserIdsLPr() {
+ return mUserIds;
+ }
+
+ private void readUserList() {
+ synchronized (mPackagesLock) {
+ readUserListLocked();
+ }
+ }
+
+ private void readUserListLocked() {
+ mGuestEnabled = false;
+ if (!mUserListFile.exists()) {
+ fallbackToSingleUserLocked();
+ return;
+ }
+ FileInputStream fis = null;
+ AtomicFile userListFile = new AtomicFile(mUserListFile);
+ try {
+ fis = userListFile.openRead();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read user list");
+ fallbackToSingleUserLocked();
+ return;
+ }
+
+ mNextSerialNumber = -1;
+ if (parser.getName().equals(TAG_USERS)) {
+ String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
+ if (lastSerialNumber != null) {
+ mNextSerialNumber = Integer.parseInt(lastSerialNumber);
+ }
+ }
+
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ UserInfo user = readUser(Integer.parseInt(id));
+ if (user != null) {
+ mUsers.put(user.id, user);
+ if (user.isGuest()) {
+ mGuestEnabled = true;
+ }
+ if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
+ mNextSerialNumber = user.id + 1;
+ }
+ }
+ }
+ }
+ updateUserIdsLocked();
+ } catch (IOException ioe) {
+ fallbackToSingleUserLocked();
+ } catch (XmlPullParserException pe) {
+ fallbackToSingleUserLocked();
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private void fallbackToSingleUserLocked() {
+ // Create the primary user
+ UserInfo primary = new UserInfo(0, "Primary", null,
+ UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+ mUsers.put(0, primary);
+ updateUserIdsLocked();
+
+ writeUserListLocked();
+ writeUserLocked(primary);
+ }
+
+ /*
+ * Writes the user file in this format:
+ *
+ * <user flags="20039023" id="0">
+ * <name>Primary</name>
+ * </user>
+ */
+ private void writeUserLocked(UserInfo userInfo) {
+ FileOutputStream fos = null;
+ AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml"));
+ try {
+ fos = userFile.startWrite();
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_USER);
+ serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
+ serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
+ serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
+ if (userInfo.iconPath != null) {
+ serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
+ }
+
+ serializer.startTag(null, TAG_NAME);
+ serializer.text(userInfo.name);
+ serializer.endTag(null, TAG_NAME);
+
+ serializer.endTag(null, TAG_USER);
+
+ serializer.endDocument();
+ userFile.finishWrite(fos);
+ } catch (Exception ioe) {
+ Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ userFile.failWrite(fos);
+ }
+ }
+
+ /*
+ * Writes the user list file in this format:
+ *
+ * <users nextSerialNumber="3">
+ * <user id="0"></user>
+ * <user id="2"></user>
+ * </users>
+ */
+ private void writeUserListLocked() {
+ FileOutputStream fos = null;
+ AtomicFile userListFile = new AtomicFile(mUserListFile);
+ try {
+ fos = userListFile.startWrite();
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_USERS);
+ serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
+
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ serializer.startTag(null, TAG_USER);
+ serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
+ serializer.endTag(null, TAG_USER);
+ }
+
+ serializer.endTag(null, TAG_USERS);
+
+ serializer.endDocument();
+ userListFile.finishWrite(fos);
+ } catch (Exception e) {
+ userListFile.failWrite(fos);
+ Slog.e(LOG_TAG, "Error writing user list");
+ }
+ }
+
+ private UserInfo readUser(int id) {
+ int flags = 0;
+ int serialNumber = id;
+ String name = null;
+ String iconPath = null;
+
+ FileInputStream fis = null;
+ try {
+ AtomicFile userFile =
+ new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml"));
+ fis = userFile.openRead();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read user " + id);
+ return null;
+ }
+
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+ String storedId = parser.getAttributeValue(null, ATTR_ID);
+ if (Integer.parseInt(storedId) != id) {
+ Slog.e(LOG_TAG, "User id does not match the file name");
+ return null;
+ }
+ String serialNumberValue = parser.getAttributeValue(null, ATTR_SERIAL_NO);
+ if (serialNumberValue != null) {
+ serialNumber = Integer.parseInt(serialNumberValue);
+ }
+ String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
+ flags = Integer.parseInt(flagString);
+ iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
+
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ name = parser.getText();
+ }
+ }
+ }
+
+ UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
+ userInfo.serialNumber = serialNumber;
+ return userInfo;
+
+ } catch (IOException ioe) {
+ } catch (XmlPullParserException pe) {
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public UserInfo createUser(String name, int flags) {
+ checkManageUsersPermission("Only the system can create users");
+ int userId = getNextAvailableId();
+ UserInfo userInfo = new UserInfo(userId, name, null, flags);
+ File userPath = new File(mBaseUserPath, Integer.toString(userId));
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
+ userInfo.serialNumber = mNextSerialNumber++;
+ mUsers.put(userId, userInfo);
+ writeUserListLocked();
+ writeUserLocked(userInfo);
+ updateUserIdsLocked();
+ mPm.createNewUserLILPw(userId, userPath);
+ }
+ }
+ if (userInfo != null) {
+ Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+ mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_BOOT_COMPLETED),
+ new UserHandle(userInfo.id));
+ }
+ return userInfo;
+ }
+
+ /**
+ * Removes a user and all data directories created for that user. This method should be called
+ * after the user's processes have been terminated.
+ * @param id the user's id
+ */
+ public boolean removeUser(int userHandle) {
+ checkManageUsersPermission("Only the system can remove users");
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
+ final UserInfo user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
+
+ // Cleanup package manager settings
+ mPm.cleanUpUserLILPw(userHandle);
+
+ // Remove this user from the list
+ mUsers.remove(userHandle);
+ // Remove user file
+ AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
+ userFile.delete();
+ // Update the user list
+ writeUserListLocked();
+ updateUserIdsLocked();
+ }
+ }
+
+ // Let other services shutdown any activity
+ Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+ return true;
+ }
+
+ @Override
+ public int getUserSerialNumber(int userHandle) {
+ synchronized (mPackagesLock) {
+ if (!exists(userHandle)) return -1;
+ return getUserInfoLocked(userHandle).serialNumber;
+ }
+ }
+
+ @Override
+ public int getUserHandle(int userSerialNumber) {
+ synchronized (mPackagesLock) {
+ for (int userId : mUserIds) {
+ if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
+ }
+ // Not found
+ return -1;
+ }
+ }
+
+ /**
+ * Caches the list of user ids in an array, adjusting the array size when necessary.
+ */
+ private void updateUserIdsLocked() {
+ int[] newUsers = new int[mUsers.size()];
+ for (int i = 0; i < mUsers.size(); i++) {
+ newUsers[i] = mUsers.keyAt(i);
+ }
+ mUserIds = newUsers;
+ }
+
+ /**
+ * Returns the next available user id, filling in any holes in the ids.
+ * TODO: May not be a good idea to recycle ids, in case it results in confusion
+ * for data and battery stats collection, or unexpected cross-talk.
+ * @return
+ */
+ private int getNextAvailableId() {
+ synchronized (mPackagesLock) {
+ int i = 0;
+ while (i < Integer.MAX_VALUE) {
+ if (mUsers.indexOfKey(i) < 0) {
+ break;
+ }
+ i++;
+ }
+ return i;
+ }
+ }
+}
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
new file mode 100644
index 0000000..cd211da
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -0,0 +1,1177 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import com.android.server.LightsService;
+import com.android.server.TwilightService;
+import com.android.server.TwilightService.TwilightState;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.SystemSensorManager;
+import android.hardware.display.DisplayManager;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.util.FloatMath;
+import android.util.Slog;
+import android.util.Spline;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * Controls the power state of the display.
+ *
+ * Handles the proximity sensor, light sensor, and animations between states
+ * including the screen off animation.
+ *
+ * This component acts independently of the rest of the power manager service.
+ * In particular, it does not share any state and it only communicates
+ * via asynchronous callbacks to inform the power manager that something has
+ * changed.
+ *
+ * Everything this class does internally is serialized on its handler although
+ * it may be accessed by other threads from the outside.
+ *
+ * Note that the power manager service guarantees that it will hold a suspend
+ * blocker as long as the display is not ready. So most of the work done here
+ * does not need to worry about holding a suspend blocker unless it happens
+ * independently of the display ready signal.
+ *
+ * For debugging, you can make the electron beam and brightness animations run
+ * slower by changing the "animator duration scale" option in Development Settings.
+ */
+final class DisplayPowerController {
+ private static final String TAG = "DisplayPowerController";
+
+ private static boolean DEBUG = false;
+ private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+ private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
+
+ // If true, uses the electron beam on animation.
+ // We might want to turn this off if we cannot get a guarantee that the screen
+ // actually turns on and starts showing new content after the call to set the
+ // screen state returns. Playing the animation can also be somewhat slow.
+ private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = false;
+
+ // If true, enables the use of the screen auto-brightness adjustment setting.
+ private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = false;
+
+ // The maximum range of gamma adjustment possible using the screen
+ // auto-brightness adjustment setting.
+ private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
+
+ // If true, enables the use of the current time as an auto-brightness adjustment.
+ // The basic idea here is to expand the dynamic range of auto-brightness
+ // when it is especially dark outside. The light sensor tends to perform
+ // poorly at low light levels so we compensate for it by making an
+ // assumption about the environment.
+ private static final boolean USE_TWILIGHT_ADJUSTMENT = true;
+
+ // Specifies the maximum magnitude of the time of day adjustment.
+ private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1.5f;
+
+ // The amount of time after or before sunrise over which to start adjusting
+ // the gamma. We want the change to happen gradually so that it is below the
+ // threshold of perceptibility and so that the adjustment has maximum effect
+ // well after dusk.
+ private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2;
+
+ private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 250;
+ private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 450;
+
+ private static final int MSG_UPDATE_POWER_STATE = 1;
+ private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
+ private static final int MSG_LIGHT_SENSOR_DEBOUNCED = 3;
+
+ private static final int PROXIMITY_UNKNOWN = -1;
+ private static final int PROXIMITY_NEGATIVE = 0;
+ private static final int PROXIMITY_POSITIVE = 1;
+
+ // Proximity sensor debounce delay in milliseconds.
+ private static final int PROXIMITY_SENSOR_DEBOUNCE_DELAY = 250;
+
+ // Trigger proximity if distance is less than 5 cm.
+ private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
+
+ // Light sensor event rate in microseconds.
+ private static final int LIGHT_SENSOR_RATE = 1000000;
+
+ // Brightness animation ramp rate in brightness units per second.
+ private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
+ private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40;
+
+ // Filter time constant in milliseconds for computing a moving
+ // average of light samples. Different constants are used
+ // to calculate the average light level when adapting to brighter or
+ // dimmer environments.
+ // This parameter only controls the filtering of light samples.
+ private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 600;
+ private static final long DIMMING_LIGHT_TIME_CONSTANT = 4000;
+
+ // Stability requirements in milliseconds for accepting a new brightness
+ // level. This is used for debouncing the light sensor. Different constants
+ // are used to debounce the light sensor when adapting to brighter or dimmer
+ // environments.
+ // This parameter controls how quickly brightness changes occur in response to
+ // an observed change in light level.
+ private static final long BRIGHTENING_LIGHT_DEBOUNCE = 2500;
+ private static final long DIMMING_LIGHT_DEBOUNCE = 10000;
+
+ private final Object mLock = new Object();
+
+ // Notifier for sending asynchronous notifications.
+ private final Notifier mNotifier;
+
+ // A suspend blocker.
+ private final SuspendBlocker mSuspendBlocker;
+
+ // Our handler.
+ private final DisplayControllerHandler mHandler;
+
+ // Asynchronous callbacks into the power manager service.
+ // Only invoked from the handler thread while no locks are held.
+ private final Callbacks mCallbacks;
+ private Handler mCallbackHandler;
+
+ // The lights service.
+ private final LightsService mLights;
+
+ // The twilight service.
+ private final TwilightService mTwilight;
+
+ // The sensor manager.
+ private final SensorManager mSensorManager;
+
+ // The proximity sensor, or null if not available or needed.
+ private Sensor mProximitySensor;
+
+ // The light sensor, or null if not available or needed.
+ private Sensor mLightSensor;
+
+ // The dim screen brightness.
+ private final int mScreenBrightnessDimConfig;
+
+ // True if auto-brightness should be used.
+ private boolean mUseSoftwareAutoBrightnessConfig;
+
+ // The auto-brightness spline adjustment.
+ // The brightness values have been scaled to a range of 0..1.
+ private Spline mScreenAutoBrightnessSpline;
+
+ // Amount of time to delay auto-brightness after screen on while waiting for
+ // the light sensor to warm-up in milliseconds.
+ // May be 0 if no warm-up is required.
+ private int mLightSensorWarmUpTimeConfig;
+
+ // The pending power request.
+ // Initially null until the first call to requestPowerState.
+ // Guarded by mLock.
+ private DisplayPowerRequest mPendingRequestLocked;
+
+ // True if a request has been made to wait for the proximity sensor to go negative.
+ // Guarded by mLock.
+ private boolean mPendingWaitForNegativeProximityLocked;
+
+ // True if the pending power request or wait for negative proximity flag
+ // has been changed since the last update occurred.
+ // Guarded by mLock.
+ private boolean mPendingRequestChangedLocked;
+
+ // Set to true when the important parts of the pending power request have been applied.
+ // The important parts are mainly the screen state. Brightness changes may occur
+ // concurrently.
+ // Guarded by mLock.
+ private boolean mDisplayReadyLocked;
+
+ // Set to true if a power state update is required.
+ // Guarded by mLock.
+ private boolean mPendingUpdatePowerStateLocked;
+
+ /* The following state must only be accessed by the handler thread. */
+
+ // The currently requested power state.
+ // The power controller will progressively update its internal state to match
+ // the requested power state. Initially null until the first update.
+ private DisplayPowerRequest mPowerRequest;
+
+ // The current power state.
+ // Must only be accessed on the handler thread.
+ private DisplayPowerState mPowerState;
+
+ // True if the device should wait for negative proximity sensor before
+ // waking up the screen. This is set to false as soon as a negative
+ // proximity sensor measurement is observed or when the device is forced to
+ // go to sleep by the user. While true, the screen remains off.
+ private boolean mWaitingForNegativeProximity;
+
+ // The actual proximity sensor threshold value.
+ private float mProximityThreshold;
+
+ // Set to true if the proximity sensor listener has been registered
+ // with the sensor manager.
+ private boolean mProximitySensorEnabled;
+
+ // The debounced proximity sensor state.
+ private int mProximity = PROXIMITY_UNKNOWN;
+
+ // The raw non-debounced proximity sensor state.
+ private int mPendingProximity = PROXIMITY_UNKNOWN;
+ private long mPendingProximityDebounceTime;
+
+ // True if the screen was turned off because of the proximity sensor.
+ // When the screen turns on again, we report user activity to the power manager.
+ private boolean mScreenOffBecauseOfProximity;
+
+ // Set to true if the light sensor is enabled.
+ private boolean mLightSensorEnabled;
+
+ // The time when the light sensor was enabled.
+ private long mLightSensorEnableTime;
+
+ // The currently accepted average light sensor value.
+ private float mLightMeasurement;
+
+ // True if the light sensor measurement is valid.
+ private boolean mLightMeasurementValid;
+
+ // The number of light sensor samples that have been collected since the
+ // last time a light sensor reading was accepted.
+ private int mRecentLightSamples;
+
+ // The moving average of recent light sensor values.
+ private float mRecentLightAverage;
+
+ // True if recent light samples are getting brighter than the previous
+ // stable light measurement.
+ private boolean mRecentLightBrightening;
+
+ // The time constant to use for filtering based on whether the
+ // light appears to be brightening or dimming.
+ private long mRecentLightTimeConstant;
+
+ // The most recent light sample.
+ private float mLastLightSample;
+
+ // The time of the most light recent sample.
+ private long mLastLightSampleTime;
+
+ // The time when we accumulated the first recent light sample into mRecentLightSamples.
+ private long mFirstRecentLightSampleTime;
+
+ // The upcoming debounce light sensor time.
+ // This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1.
+ private long mPendingLightSensorDebounceTime;
+
+ // The screen brightness level that has been chosen by the auto-brightness
+ // algorithm. The actual brightness should ramp towards this value.
+ // We preserve this value even when we stop using the light sensor so
+ // that we can quickly revert to the previous auto-brightness level
+ // while the light sensor warms up.
+ // Use -1 if there is no current auto-brightness value available.
+ private int mScreenAutoBrightness = -1;
+
+ // The last screen auto-brightness gamma. (For printing in dump() only.)
+ private float mLastScreenAutoBrightnessGamma = 1.0f;
+
+ // True if the screen auto-brightness value is actually being used to
+ // set the display brightness.
+ private boolean mUsingScreenAutoBrightness;
+
+ // Animators.
+ private ObjectAnimator mElectronBeamOnAnimator;
+ private ObjectAnimator mElectronBeamOffAnimator;
+ private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+
+ // Twilight changed. We might recalculate auto-brightness values.
+ private boolean mTwilightChanged;
+
+ /**
+ * Creates the display power controller.
+ */
+ public DisplayPowerController(Looper looper, Context context, Notifier notifier,
+ LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker,
+ Callbacks callbacks, Handler callbackHandler) {
+ mHandler = new DisplayControllerHandler(looper);
+ mNotifier = notifier;
+ mSuspendBlocker = suspendBlocker;
+ mCallbacks = callbacks;
+ mCallbackHandler = callbackHandler;
+
+ mLights = lights;
+ mTwilight = twilight;
+ mSensorManager = new SystemSensorManager(mHandler.getLooper());
+
+ final Resources resources = context.getResources();
+ mScreenBrightnessDimConfig = resources.getInteger(
+ com.android.internal.R.integer.config_screenBrightnessDim);
+ mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available);
+ if (mUseSoftwareAutoBrightnessConfig) {
+ int[] lux = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels);
+ int[] screenBrightness = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+
+ mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
+ if (mScreenAutoBrightnessSpline == null) {
+ Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
+ + "(size " + screenBrightness.length + ") "
+ + "must be monotic and have exactly one more entry than "
+ + "config_autoBrightnessLevels (size " + lux.length + ") "
+ + "which must be strictly increasing. "
+ + "Auto-brightness will be disabled.");
+ mUseSoftwareAutoBrightnessConfig = false;
+ }
+
+ mLightSensorWarmUpTimeConfig = resources.getInteger(
+ com.android.internal.R.integer.config_lightSensorWarmupTime);
+ }
+
+ if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (mProximitySensor != null) {
+ mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
+ TYPICAL_PROXIMITY_THRESHOLD);
+ }
+ }
+
+ if (mUseSoftwareAutoBrightnessConfig
+ && !DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
+ mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ }
+
+ if (mUseSoftwareAutoBrightnessConfig && USE_TWILIGHT_ADJUSTMENT) {
+ mTwilight.registerListener(mTwilightListener, mHandler);
+ }
+ }
+
+ private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
+ try {
+ final int n = brightness.length;
+ float[] x = new float[n];
+ float[] y = new float[n];
+ y[0] = (float)brightness[0] / PowerManager.BRIGHTNESS_ON;
+ for (int i = 1; i < n; i++) {
+ x[i] = lux[i - 1];
+ y[i] = (float)brightness[i] / PowerManager.BRIGHTNESS_ON;
+ }
+
+ Spline spline = Spline.createMonotoneCubicSpline(x, y);
+ if (false) {
+ Slog.d(TAG, "Auto-brightness spline: " + spline);
+ for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
+ Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
+ }
+ }
+ return spline;
+ } catch (IllegalArgumentException ex) {
+ Slog.e(TAG, "Could not create auto-brightness spline.", ex);
+ return null;
+ }
+ }
+
+ /**
+ * Returns true if the proximity sensor screen-off function is available.
+ */
+ public boolean isProximitySensorAvailable() {
+ return mProximitySensor != null;
+ }
+
+ /**
+ * Requests a new power state.
+ * The controller makes a copy of the provided object and then
+ * begins adjusting the power state to match what was requested.
+ *
+ * @param request The requested power state.
+ * @param waitForNegativeProximity If true, issues a request to wait for
+ * negative proximity before turning the screen back on, assuming the screen
+ * was turned off by the proximity sensor.
+ * @return True if display is ready, false if there are important changes that must
+ * be made asynchronously (such as turning the screen on), in which case the caller
+ * should grab a wake lock, watch for {@link Callbacks#onStateChanged()} then try
+ * the request again later until the state converges.
+ */
+ public boolean requestPowerState(DisplayPowerRequest request,
+ boolean waitForNegativeProximity) {
+ if (DEBUG) {
+ Slog.d(TAG, "requestPowerState: "
+ + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
+ }
+
+ synchronized (mLock) {
+ boolean changed = false;
+
+ if (waitForNegativeProximity
+ && !mPendingWaitForNegativeProximityLocked) {
+ mPendingWaitForNegativeProximityLocked = true;
+ changed = true;
+ }
+
+ if (mPendingRequestLocked == null) {
+ mPendingRequestLocked = new DisplayPowerRequest(request);
+ changed = true;
+ } else if (!mPendingRequestLocked.equals(request)) {
+ mPendingRequestLocked.copyFrom(request);
+ changed = true;
+ }
+
+ if (changed) {
+ mDisplayReadyLocked = false;
+ }
+
+ if (changed && !mPendingRequestChangedLocked) {
+ mPendingRequestChangedLocked = true;
+ sendUpdatePowerStateLocked();
+ }
+
+ return mDisplayReadyLocked;
+ }
+ }
+
+ private void sendUpdatePowerState() {
+ synchronized (mLock) {
+ sendUpdatePowerStateLocked();
+ }
+ }
+
+ private void sendUpdatePowerStateLocked() {
+ if (!mPendingUpdatePowerStateLocked) {
+ mPendingUpdatePowerStateLocked = true;
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private void initialize() {
+ final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
+ Display display = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ mPowerState = new DisplayPowerState(new ElectronBeam(display),
+ new PhotonicModulator(executor,
+ mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
+ mSuspendBlocker));
+
+ mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
+ mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
+ mElectronBeamOnAnimator.setDuration(ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS);
+ mElectronBeamOnAnimator.addListener(mAnimatorListener);
+
+ mElectronBeamOffAnimator = ObjectAnimator.ofFloat(
+ mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 1.0f, 0.0f);
+ mElectronBeamOffAnimator.setDuration(ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS);
+ mElectronBeamOffAnimator.addListener(mAnimatorListener);
+
+ mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
+ mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
+ }
+
+ private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ sendUpdatePowerState();
+ }
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+ };
+
+ private void updatePowerState() {
+ // Update the power state request.
+ final boolean mustNotify;
+ boolean mustInitialize = false;
+ boolean updateAutoBrightness = mTwilightChanged;
+ mTwilightChanged = false;
+
+ synchronized (mLock) {
+ mPendingUpdatePowerStateLocked = false;
+ if (mPendingRequestLocked == null) {
+ return; // wait until first actual power request
+ }
+
+ if (mPowerRequest == null) {
+ mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
+ mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
+ mPendingWaitForNegativeProximityLocked = false;
+ mPendingRequestChangedLocked = false;
+ mustInitialize = true;
+ } else if (mPendingRequestChangedLocked) {
+ if (mPowerRequest.screenAutoBrightnessAdjustment
+ != mPendingRequestLocked.screenAutoBrightnessAdjustment) {
+ updateAutoBrightness = true;
+ }
+ mPowerRequest.copyFrom(mPendingRequestLocked);
+ mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
+ mPendingWaitForNegativeProximityLocked = false;
+ mPendingRequestChangedLocked = false;
+ mDisplayReadyLocked = false;
+ }
+
+ mustNotify = !mDisplayReadyLocked;
+ }
+
+ // Initialize things the first time the power state is changed.
+ if (mustInitialize) {
+ initialize();
+ }
+
+ // Apply the proximity sensor.
+ if (mProximitySensor != null) {
+ if (mPowerRequest.useProximitySensor
+ && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+ setProximitySensorEnabled(true);
+ if (!mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE) {
+ mScreenOffBecauseOfProximity = true;
+ setScreenOn(false);
+ }
+ } else if (mWaitingForNegativeProximity
+ && mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE
+ && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+ setProximitySensorEnabled(true);
+ } else {
+ setProximitySensorEnabled(false);
+ mWaitingForNegativeProximity = false;
+ }
+ if (mScreenOffBecauseOfProximity
+ && mProximity != PROXIMITY_POSITIVE) {
+ mScreenOffBecauseOfProximity = false;
+ setScreenOn(true);
+ sendOnProximityNegative();
+ }
+ } else {
+ mWaitingForNegativeProximity = false;
+ }
+
+ // Turn on the light sensor if needed.
+ if (mLightSensor != null) {
+ setLightSensorEnabled(mPowerRequest.useAutoBrightness
+ && wantScreenOn(mPowerRequest.screenState), updateAutoBrightness);
+ }
+
+ // Set the screen brightness.
+ if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
+ // Screen is dimmed. Overrides everything else.
+ animateScreenBrightness(
+ clampScreenBrightness(mScreenBrightnessDimConfig),
+ BRIGHTNESS_RAMP_RATE_FAST);
+ mUsingScreenAutoBrightness = false;
+ } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) {
+ if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
+ // Use current auto-brightness value.
+ animateScreenBrightness(
+ clampScreenBrightness(mScreenAutoBrightness),
+ mUsingScreenAutoBrightness ? BRIGHTNESS_RAMP_RATE_SLOW :
+ BRIGHTNESS_RAMP_RATE_FAST);
+ mUsingScreenAutoBrightness = true;
+ } else {
+ // Light sensor is disabled or not ready yet.
+ // Use the current brightness setting from the request, which is expected
+ // provide a nominal default value for the case where auto-brightness
+ // is not ready yet.
+ animateScreenBrightness(
+ clampScreenBrightness(mPowerRequest.screenBrightness),
+ BRIGHTNESS_RAMP_RATE_FAST);
+ mUsingScreenAutoBrightness = false;
+ }
+ } else {
+ // Screen is off. Don't bother changing the brightness.
+ mUsingScreenAutoBrightness = false;
+ }
+
+ // Animate the screen on or off.
+ if (!mScreenOffBecauseOfProximity) {
+ if (wantScreenOn(mPowerRequest.screenState)) {
+ // Want screen on.
+ // Wait for previous off animation to complete beforehand.
+ // It is relatively short but if we cancel it and switch to the
+ // on animation immediately then the results are pretty ugly.
+ if (!mElectronBeamOffAnimator.isStarted()) {
+ setScreenOn(true);
+ if (USE_ELECTRON_BEAM_ON_ANIMATION) {
+ if (!mElectronBeamOnAnimator.isStarted()) {
+ if (mPowerState.getElectronBeamLevel() == 1.0f) {
+ mPowerState.dismissElectronBeam();
+ } else if (mPowerState.prepareElectronBeam(true)) {
+ mElectronBeamOnAnimator.start();
+ } else {
+ mElectronBeamOnAnimator.end();
+ }
+ }
+ } else {
+ mPowerState.setElectronBeamLevel(1.0f);
+ mPowerState.dismissElectronBeam();
+ }
+ }
+ } else {
+ // Want screen off.
+ // Wait for previous on animation to complete beforehand.
+ if (!mElectronBeamOnAnimator.isStarted()) {
+ if (!mElectronBeamOffAnimator.isStarted()) {
+ if (mPowerState.getElectronBeamLevel() == 0.0f) {
+ setScreenOn(false);
+ } else if (mPowerState.prepareElectronBeam(false)
+ && mPowerState.isScreenOn()) {
+ mElectronBeamOffAnimator.start();
+ } else {
+ mElectronBeamOffAnimator.end();
+ }
+ }
+ }
+ }
+ }
+
+ // Report whether the display is ready for use.
+ // We mostly care about the screen state here, ignoring brightness changes
+ // which will be handled asynchronously.
+ if (mustNotify
+ && !mElectronBeamOnAnimator.isStarted()
+ && !mElectronBeamOffAnimator.isStarted()
+ && mPowerState.waitUntilClean(mCleanListener)) {
+ synchronized (mLock) {
+ if (!mPendingRequestChangedLocked) {
+ mDisplayReadyLocked = true;
+ }
+ }
+ sendOnStateChanged();
+ }
+ }
+
+ private void setScreenOn(boolean on) {
+ if (!mPowerState.isScreenOn() == on) {
+ mPowerState.setScreenOn(on);
+ if (on) {
+ mNotifier.onScreenOn();
+ } else {
+ mNotifier.onScreenOff();
+ }
+ }
+ }
+
+ private int clampScreenBrightness(int value) {
+ return Math.min(Math.max(Math.max(value, mScreenBrightnessDimConfig), 0), 255);
+ }
+
+ private void animateScreenBrightness(int target, int rate) {
+ if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
+ mNotifier.onScreenBrightness(target);
+ }
+ }
+
+ private final Runnable mCleanListener = new Runnable() {
+ @Override
+ public void run() {
+ sendUpdatePowerState();
+ }
+ };
+
+ private void setProximitySensorEnabled(boolean enable) {
+ if (enable) {
+ if (!mProximitySensorEnabled) {
+ mProximitySensorEnabled = true;
+ mPendingProximity = PROXIMITY_UNKNOWN;
+ mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
+ SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+ }
+ } else {
+ if (mProximitySensorEnabled) {
+ mProximitySensorEnabled = false;
+ mProximity = PROXIMITY_UNKNOWN;
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mSensorManager.unregisterListener(mProximitySensorListener);
+ }
+ }
+ }
+
+ private void handleProximitySensorEvent(long time, boolean positive) {
+ if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
+ return; // no change
+ }
+ if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
+ return; // no change
+ }
+
+ // Only accept a proximity sensor reading if it remains
+ // stable for the entire debounce delay.
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mPendingProximity = positive ? PROXIMITY_POSITIVE : PROXIMITY_NEGATIVE;
+ mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_DEBOUNCE_DELAY;
+ debounceProximitySensor();
+ }
+
+ private void debounceProximitySensor() {
+ if (mPendingProximity != PROXIMITY_UNKNOWN) {
+ final long now = SystemClock.uptimeMillis();
+ if (mPendingProximityDebounceTime <= now) {
+ mProximity = mPendingProximity;
+ sendUpdatePowerState();
+ } else {
+ Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
+ }
+ }
+ }
+
+ private void setLightSensorEnabled(boolean enable, boolean updateAutoBrightness) {
+ if (enable) {
+ if (!mLightSensorEnabled) {
+ updateAutoBrightness = true;
+ mLightSensorEnabled = true;
+ mLightSensorEnableTime = SystemClock.uptimeMillis();
+ mSensorManager.registerListener(mLightSensorListener, mLightSensor,
+ LIGHT_SENSOR_RATE, mHandler);
+ }
+ } else {
+ if (mLightSensorEnabled) {
+ mLightSensorEnabled = false;
+ mLightMeasurementValid = false;
+ mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
+ mSensorManager.unregisterListener(mLightSensorListener);
+ }
+ }
+ if (updateAutoBrightness) {
+ updateAutoBrightness(false);
+ }
+ }
+
+ private void handleLightSensorEvent(long time, float lux) {
+ // Take the first few readings during the warm-up period and apply them
+ // immediately without debouncing.
+ if (!mLightMeasurementValid
+ || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) {
+ mLightMeasurement = lux;
+ mLightMeasurementValid = true;
+ mRecentLightSamples = 0;
+ updateAutoBrightness(true);
+ }
+
+ // Update our moving average.
+ if (lux != mLightMeasurement && (mRecentLightSamples == 0
+ || (lux < mLightMeasurement && mRecentLightBrightening)
+ || (lux > mLightMeasurement && !mRecentLightBrightening))) {
+ // If the newest light sample doesn't seem to be going in the
+ // same general direction as recent samples, then start over.
+ setRecentLight(time, lux, lux > mLightMeasurement);
+ } else if (mRecentLightSamples >= 1) {
+ // Add the newest light sample to the moving average.
+ accumulateRecentLight(time, lux);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "handleLightSensorEvent: lux=" + lux
+ + ", mLightMeasurementValid=" + mLightMeasurementValid
+ + ", mLightMeasurement=" + mLightMeasurement
+ + ", mRecentLightSamples=" + mRecentLightSamples
+ + ", mRecentLightAverage=" + mRecentLightAverage
+ + ", mRecentLightBrightening=" + mRecentLightBrightening
+ + ", mRecentLightTimeConstant=" + mRecentLightTimeConstant
+ + ", mFirstRecentLightSampleTime="
+ + TimeUtils.formatUptime(mFirstRecentLightSampleTime)
+ + ", mPendingLightSensorDebounceTime="
+ + TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
+ }
+
+ // Debounce.
+ mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
+ debounceLightSensor();
+ }
+
+ private void setRecentLight(long time, float lux, boolean brightening) {
+ mRecentLightBrightening = brightening;
+ mRecentLightTimeConstant = brightening ?
+ BRIGHTENING_LIGHT_TIME_CONSTANT : DIMMING_LIGHT_TIME_CONSTANT;
+ mRecentLightSamples = 1;
+ mRecentLightAverage = lux;
+ mLastLightSample = lux;
+ mLastLightSampleTime = time;
+ mFirstRecentLightSampleTime = time;
+ mPendingLightSensorDebounceTime = time + (brightening ?
+ BRIGHTENING_LIGHT_DEBOUNCE : DIMMING_LIGHT_DEBOUNCE);
+ }
+
+ private void accumulateRecentLight(long time, float lux) {
+ final long timeDelta = time - mLastLightSampleTime;
+ mRecentLightSamples += 1;
+ mRecentLightAverage += (lux - mRecentLightAverage) *
+ timeDelta / (mRecentLightTimeConstant + timeDelta);
+ mLastLightSample = lux;
+ mLastLightSampleTime = time;
+ }
+
+ private void debounceLightSensor() {
+ if (mLightMeasurementValid && mRecentLightSamples >= 1) {
+ final long now = SystemClock.uptimeMillis();
+ if (mPendingLightSensorDebounceTime <= now) {
+ accumulateRecentLight(now, mLastLightSample);
+ mLightMeasurement = mRecentLightAverage;
+
+ if (DEBUG) {
+ Slog.d(TAG, "debounceLightSensor: Accepted new measurement "
+ + mLightMeasurement + " after "
+ + (now - mFirstRecentLightSampleTime) + " ms based on "
+ + mRecentLightSamples + " recent samples.");
+ }
+
+ updateAutoBrightness(true);
+
+ // Now that we have debounced the light sensor data, we have the
+ // option of either leaving the sensor in a debounced state or
+ // restarting the debounce cycle by setting mRecentLightSamples to 0.
+ //
+ // If we leave the sensor debounced, then new average light measurements
+ // may be accepted immediately as long as they are trending in the same
+ // direction as they were before. If the measurements start
+ // jittering or trending in the opposite direction then the debounce
+ // cycle will automatically be restarted. The benefit is that the
+ // auto-brightness control can be more responsive to changes over a
+ // broad range.
+ //
+ // For now, we choose to be more responsive and leave the following line
+ // commented out.
+ //
+ // mRecentLightSamples = 0;
+ } else {
+ Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime);
+ }
+ }
+ }
+
+ private void updateAutoBrightness(boolean sendUpdate) {
+ if (!mLightMeasurementValid) {
+ return;
+ }
+
+ float value = mScreenAutoBrightnessSpline.interpolate(mLightMeasurement);
+ float gamma = 1.0f;
+
+ if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
+ && mPowerRequest.screenAutoBrightnessAdjustment != 0.0f) {
+ final float adjGamma = FloatMath.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
+ Math.min(1.0f, Math.max(-1.0f,
+ -mPowerRequest.screenAutoBrightnessAdjustment)));
+ gamma *= adjGamma;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
+ }
+ }
+
+ if (USE_TWILIGHT_ADJUSTMENT) {
+ TwilightState state = mTwilight.getCurrentState();
+ if (state != null && state.isNight()) {
+ final long now = System.currentTimeMillis();
+ final float earlyGamma =
+ getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
+ final float lateGamma =
+ getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
+ gamma *= earlyGamma * lateGamma;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
+ + ", lateGamma=" + lateGamma);
+ }
+ }
+ }
+
+ if (gamma != 1.0f) {
+ final float in = value;
+ value = FloatMath.pow(value, gamma);
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
+ + ", in=" + in + ", out=" + value);
+ }
+ }
+
+ int newScreenAutoBrightness = clampScreenBrightness(
+ (int)Math.round(value * PowerManager.BRIGHTNESS_ON));
+ if (mScreenAutoBrightness != newScreenAutoBrightness) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
+ + mScreenAutoBrightness + ", newScreenAutoBrightness="
+ + newScreenAutoBrightness);
+ }
+
+ mScreenAutoBrightness = newScreenAutoBrightness;
+ mLastScreenAutoBrightnessGamma = gamma;
+ if (sendUpdate) {
+ sendUpdatePowerState();
+ }
+ }
+ }
+
+ private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) {
+ if (lastSunset < 0 || nextSunrise < 0
+ || now < lastSunset || now > nextSunrise) {
+ return 1.0f;
+ }
+
+ if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
+ return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+ (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
+ return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+ (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
+ }
+
+ private static float lerp(float x, float y, float alpha) {
+ return x + (y - x) * alpha;
+ }
+
+ private void sendOnStateChanged() {
+ mCallbackHandler.post(mOnStateChangedRunnable);
+ }
+
+ private final Runnable mOnStateChangedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCallbacks.onStateChanged();
+ }
+ };
+
+ private void sendOnProximityNegative() {
+ mCallbackHandler.post(mOnProximityNegativeRunnable);
+ }
+
+ private final Runnable mOnProximityNegativeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCallbacks.onProximityNegative();
+ }
+ };
+
+ public void dump(PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("Display Controller Locked State:");
+ pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked);
+ pw.println(" mPendingRequestLocked=" + mPendingRequestLocked);
+ pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
+ pw.println(" mPendingWaitForNegativeProximityLocked="
+ + mPendingWaitForNegativeProximityLocked);
+ pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
+ }
+
+ pw.println();
+ pw.println("Display Controller Configuration:");
+ pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
+ pw.println(" mUseSoftwareAutoBrightnessConfig="
+ + mUseSoftwareAutoBrightnessConfig);
+ pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
+ pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ dumpLocal(pw);
+ } else {
+ final StringWriter out = new StringWriter();
+ final CountDownLatch latch = new CountDownLatch(1);
+ Message msg = Message.obtain(mHandler, new Runnable() {
+ @Override
+ public void run() {
+ PrintWriter localpw = new PrintWriter(out);
+ try {
+ dumpLocal(localpw);
+ } finally {
+ localpw.flush();
+ latch.countDown();
+ }
+ }
+ });
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ try {
+ latch.await();
+ pw.print(out.toString());
+ } catch (InterruptedException ex) {
+ pw.println();
+ pw.println("Failed to dump thread state due to interrupted exception!");
+ }
+ }
+ }
+
+ private void dumpLocal(PrintWriter pw) {
+ pw.println();
+ pw.println("Display Controller Thread State:");
+ pw.println(" mPowerRequest=" + mPowerRequest);
+ pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
+
+ pw.println(" mProximitySensor=" + mProximitySensor);
+ pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
+ pw.println(" mProximityThreshold=" + mProximityThreshold);
+ pw.println(" mProximity=" + proximityToString(mProximity));
+ pw.println(" mPendingProximity=" + proximityToString(mPendingProximity));
+ pw.println(" mPendingProximityDebounceTime="
+ + TimeUtils.formatUptime(mPendingProximityDebounceTime));
+ pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
+
+ pw.println(" mLightSensor=" + mLightSensor);
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mLightSensorEnableTime="
+ + TimeUtils.formatUptime(mLightSensorEnableTime));
+ pw.println(" mLightMeasurement=" + mLightMeasurement);
+ pw.println(" mLightMeasurementValid=" + mLightMeasurementValid);
+ pw.println(" mLastLightSample=" + mLastLightSample);
+ pw.println(" mLastLightSampleTime="
+ + TimeUtils.formatUptime(mLastLightSampleTime));
+ pw.println(" mRecentLightSamples=" + mRecentLightSamples);
+ pw.println(" mRecentLightAverage=" + mRecentLightAverage);
+ pw.println(" mRecentLightBrightening=" + mRecentLightBrightening);
+ pw.println(" mRecentLightTimeConstant=" + mRecentLightTimeConstant);
+ pw.println(" mFirstRecentLightSampleTime="
+ + TimeUtils.formatUptime(mFirstRecentLightSampleTime));
+ pw.println(" mPendingLightSensorDebounceTime="
+ + TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
+ pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
+ pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
+ pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
+ pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
+
+ if (mElectronBeamOnAnimator != null) {
+ pw.println(" mElectronBeamOnAnimator.isStarted()=" +
+ mElectronBeamOnAnimator.isStarted());
+ }
+ if (mElectronBeamOffAnimator != null) {
+ pw.println(" mElectronBeamOffAnimator.isStarted()=" +
+ mElectronBeamOffAnimator.isStarted());
+ }
+
+ if (mPowerState != null) {
+ mPowerState.dump(pw);
+ }
+ }
+
+ private static String proximityToString(int state) {
+ switch (state) {
+ case PROXIMITY_UNKNOWN:
+ return "Unknown";
+ case PROXIMITY_NEGATIVE:
+ return "Negative";
+ case PROXIMITY_POSITIVE:
+ return "Positive";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ private static boolean wantScreenOn(int state) {
+ switch (state) {
+ case DisplayPowerRequest.SCREEN_STATE_BRIGHT:
+ case DisplayPowerRequest.SCREEN_STATE_DIM:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Asynchronous callbacks from the power controller to the power manager service.
+ */
+ public interface Callbacks {
+ void onStateChanged();
+ void onProximityNegative();
+ }
+
+ private final class DisplayControllerHandler extends Handler {
+ public DisplayControllerHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_POWER_STATE:
+ updatePowerState();
+ break;
+
+ case MSG_PROXIMITY_SENSOR_DEBOUNCED:
+ debounceProximitySensor();
+ break;
+
+ case MSG_LIGHT_SENSOR_DEBOUNCED:
+ debounceLightSensor();
+ break;
+ }
+ }
+ }
+
+ private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mProximitySensorEnabled) {
+ final long time = SystemClock.uptimeMillis();
+ final float distance = event.values[0];
+ boolean positive = distance >= 0.0f && distance < mProximityThreshold;
+ handleProximitySensorEvent(time, positive);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ private final SensorEventListener mLightSensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mLightSensorEnabled) {
+ final long time = SystemClock.uptimeMillis();
+ final float lux = event.values[0];
+ handleLightSensorEvent(time, lux);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ private final TwilightService.TwilightListener mTwilightListener =
+ new TwilightService.TwilightListener() {
+ @Override
+ public void onTwilightStateChanged() {
+ mTwilightChanged = true;
+ updatePowerState();
+ }
+ };
+}
diff --git a/services/java/com/android/server/power/DisplayPowerRequest.java b/services/java/com/android/server/power/DisplayPowerRequest.java
new file mode 100644
index 0000000..2d74292
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerRequest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.os.PowerManager;
+
+/**
+ * Describes the requested power state of the display.
+ *
+ * This object is intended to describe the general characteristics of the
+ * power state, such as whether the screen should be on or off and the current
+ * brightness controls leaving the {@link DisplayPowerController} to manage the
+ * details of how the transitions between states should occur. The goal is for
+ * the {@link PowerManagerService} to focus on the global power state and not
+ * have to micro-manage screen off animations, auto-brightness and other effects.
+ */
+final class DisplayPowerRequest {
+ public static final int SCREEN_STATE_OFF = 0;
+ public static final int SCREEN_STATE_DIM = 1;
+ public static final int SCREEN_STATE_BRIGHT = 2;
+
+ // The requested minimum screen power state: off, dim or bright.
+ public int screenState;
+
+ // If true, the proximity sensor overrides the screen state when an object is
+ // nearby, turning it off temporarily until the object is moved away.
+ public boolean useProximitySensor;
+
+ // The desired screen brightness in the range 0 (minimum / off) to 255 (brightest).
+ // The display power controller may choose to clamp the brightness.
+ // When auto-brightness is enabled, this field should specify a nominal default
+ // value to use while waiting for the light sensor to report enough data.
+ public int screenBrightness;
+
+ // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter).
+ public float screenAutoBrightnessAdjustment;
+
+ // If true, enables automatic brightness control.
+ public boolean useAutoBrightness;
+
+ public DisplayPowerRequest() {
+ screenState = SCREEN_STATE_BRIGHT;
+ useProximitySensor = false;
+ screenBrightness = PowerManager.BRIGHTNESS_ON;
+ screenAutoBrightnessAdjustment = 0.0f;
+ useAutoBrightness = false;
+ }
+
+ public DisplayPowerRequest(DisplayPowerRequest other) {
+ copyFrom(other);
+ }
+
+ public void copyFrom(DisplayPowerRequest other) {
+ screenState = other.screenState;
+ useProximitySensor = other.useProximitySensor;
+ screenBrightness = other.screenBrightness;
+ screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
+ useAutoBrightness = other.useAutoBrightness;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof DisplayPowerRequest
+ && equals((DisplayPowerRequest)o);
+ }
+
+ public boolean equals(DisplayPowerRequest other) {
+ return other != null
+ && screenState == other.screenState
+ && useProximitySensor == other.useProximitySensor
+ && screenBrightness == other.screenBrightness
+ && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
+ && useAutoBrightness == other.useAutoBrightness;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
+ }
+
+ @Override
+ public String toString() {
+ return "screenState=" + screenState
+ + ", useProximitySensor=" + useProximitySensor
+ + ", screenBrightness=" + screenBrightness
+ + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
+ + ", useAutoBrightness=" + useAutoBrightness;
+ }
+}
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
new file mode 100644
index 0000000..3524a08
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.os.Looper;
+import android.os.PowerManager;
+import android.util.FloatProperty;
+import android.util.IntProperty;
+import android.util.Slog;
+import android.view.Choreographer;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents the current display power state and realizes it.
+ *
+ * This component is similar in nature to a {@link View} except that it describes
+ * the properties of a display. When properties are changed, the component
+ * invalidates itself and posts a callback to the {@link Choreographer} to
+ * apply the changes. This mechanism enables the display power state to be
+ * animated smoothly by the animation framework.
+ *
+ * This component must only be created or accessed by the {@link Looper} thread
+ * that belongs to the {@link DisplayPowerController}.
+ *
+ * We don't need to worry about holding a suspend blocker here because the
+ * {@link DisplayPowerController} does that for us whenever there is a pending invalidate.
+ */
+final class DisplayPowerState {
+ private static final String TAG = "DisplayPowerState";
+
+ private static boolean DEBUG = false;
+
+ private static final int DIRTY_SCREEN_ON = 1 << 0;
+ private static final int DIRTY_ELECTRON_BEAM = 1 << 1;
+ private static final int DIRTY_BRIGHTNESS = 1 << 2;
+
+ private final Choreographer mChoreographer;
+ private final ElectronBeam mElectronBeam;
+ private final PhotonicModulator mScreenBrightnessModulator;
+
+ private int mDirty;
+ private boolean mScreenOn;
+ private float mElectronBeamLevel;
+ private int mScreenBrightness;
+
+ private Runnable mCleanListener;
+
+ public DisplayPowerState(ElectronBeam electronBean,
+ PhotonicModulator screenBrightnessModulator) {
+ mChoreographer = Choreographer.getInstance();
+ mElectronBeam = electronBean;
+ mScreenBrightnessModulator = screenBrightnessModulator;
+
+ // At boot time, we know that the screen is on and the electron beam
+ // animation is not playing. We don't know the screen's brightness though,
+ // so prepare to set it to a known state when the state is next applied.
+ // Although we set the brightness to full on here, the display power controller
+ // will reset the brightness to a new level immediately before the changes
+ // actually have a chance to be applied.
+ mScreenOn = true;
+ mElectronBeamLevel = 1.0f;
+ mScreenBrightness = PowerManager.BRIGHTNESS_ON;
+ invalidate(DIRTY_BRIGHTNESS);
+ }
+
+ public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
+ new FloatProperty<DisplayPowerState>("electronBeamLevel") {
+ @Override
+ public void setValue(DisplayPowerState object, float value) {
+ object.setElectronBeamLevel(value);
+ }
+
+ @Override
+ public Float get(DisplayPowerState object) {
+ return object.getElectronBeamLevel();
+ }
+ };
+
+ public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
+ new IntProperty<DisplayPowerState>("screenBrightness") {
+ @Override
+ public void setValue(DisplayPowerState object, int value) {
+ object.setScreenBrightness(value);
+ }
+
+ @Override
+ public Integer get(DisplayPowerState object) {
+ return object.getScreenBrightness();
+ }
+ };
+
+ /**
+ * Sets whether the screen is on or off.
+ */
+ public void setScreenOn(boolean on) {
+ if (mScreenOn != on) {
+ if (DEBUG) {
+ Slog.d(TAG, "setScreenOn: on=" + on);
+ }
+
+ mScreenOn = on;
+ invalidate(DIRTY_SCREEN_ON);
+ }
+ }
+
+ /**
+ * Returns true if the screen is on.
+ */
+ public boolean isScreenOn() {
+ return mScreenOn;
+ }
+
+ /**
+ * Prepares the electron beam to turn on or off.
+ * This method should be called before starting an animation because it
+ * can take a fair amount of time to prepare the electron beam surface.
+ *
+ * @param warmUp True if the electron beam should start warming up.
+ * @return True if the electron beam was prepared.
+ */
+ public boolean prepareElectronBeam(boolean warmUp) {
+ boolean success = mElectronBeam.prepare(warmUp);
+ invalidate(DIRTY_ELECTRON_BEAM);
+ return success;
+ }
+
+ /**
+ * Dismisses the electron beam surface.
+ */
+ public void dismissElectronBeam() {
+ mElectronBeam.dismiss();
+ }
+
+ /**
+ * Sets the level of the electron beam steering current.
+ *
+ * The display is blanked when the level is 0.0. In normal use, the electron
+ * beam should have a value of 1.0. The electron beam is unstable in between
+ * these states and the picture quality may be compromised. For best effect,
+ * the electron beam should be warmed up or cooled off slowly.
+ *
+ * Warning: Electron beam emits harmful radiation. Avoid direct exposure to
+ * skin or eyes.
+ *
+ * @param level The level, ranges from 0.0 (full off) to 1.0 (full on).
+ */
+ public void setElectronBeamLevel(float level) {
+ if (mElectronBeamLevel != level) {
+ if (DEBUG) {
+ Slog.d(TAG, "setElectronBeamLevel: level=" + level);
+ }
+
+ mElectronBeamLevel = level;
+ invalidate(DIRTY_ELECTRON_BEAM);
+ }
+ }
+
+ /**
+ * Gets the level of the electron beam steering current.
+ */
+ public float getElectronBeamLevel() {
+ return mElectronBeamLevel;
+ }
+
+ /**
+ * Sets the display brightness.
+ *
+ * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
+ */
+ public void setScreenBrightness(int brightness) {
+ if (mScreenBrightness != brightness) {
+ if (DEBUG) {
+ Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
+ }
+
+ mScreenBrightness = brightness;
+ invalidate(DIRTY_BRIGHTNESS);
+ }
+ }
+
+ /**
+ * Gets the screen brightness.
+ */
+ public int getScreenBrightness() {
+ return mScreenBrightness;
+ }
+
+ /**
+ * Returns true if no properties have been invalidated.
+ * Otherwise, returns false and promises to invoke the specified listener
+ * when the properties have all been applied.
+ * The listener always overrides any previously set listener.
+ */
+ public boolean waitUntilClean(Runnable listener) {
+ if (mDirty != 0) {
+ mCleanListener = listener;
+ return false;
+ } else {
+ mCleanListener = null;
+ return true;
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("Display Power State:");
+ pw.println(" mDirty=" + Integer.toHexString(mDirty));
+ pw.println(" mScreenOn=" + mScreenOn);
+ pw.println(" mScreenBrightness=" + mScreenBrightness);
+ pw.println(" mElectronBeamLevel=" + mElectronBeamLevel);
+
+ mElectronBeam.dump(pw);
+ }
+
+ private void invalidate(int dirty) {
+ if (mDirty == 0) {
+ mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
+ mTraversalRunnable, null);
+ }
+
+ mDirty |= dirty;
+ }
+
+ private void apply() {
+ if (mDirty != 0) {
+ if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) {
+ PowerManagerService.nativeSetScreenState(false);
+ }
+
+ if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
+ mElectronBeam.draw(mElectronBeamLevel);
+ }
+
+ if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0) {
+ mScreenBrightnessModulator.setBrightness(mScreenOn ?
+ (int)(mScreenBrightness * mElectronBeamLevel) : 0);
+ }
+
+ if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
+ PowerManagerService.nativeSetScreenState(true);
+ }
+
+ mDirty = 0;
+
+ if (mCleanListener != null) {
+ mCleanListener.run();
+ }
+ }
+ }
+
+ private final Runnable mTraversalRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mDirty != 0) {
+ apply();
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java
new file mode 100644
index 0000000..aad5a9a
--- /dev/null
+++ b/services/java/com/android/server/power/ElectronBeam.java
@@ -0,0 +1,652 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.opengl.GLES10;
+import android.opengl.GLUtils;
+import android.os.Looper;
+import android.os.Process;
+import android.util.FloatMath;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceSession;
+
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * Bzzzoooop! *crackle*
+ *
+ * Animates a screen transition from on to off or off to on by applying
+ * some GL transformations to a screenshot.
+ *
+ * This component must only be created or accessed by the {@link Looper} thread
+ * that belongs to the {@link DisplayPowerController}.
+ */
+final class ElectronBeam {
+ private static final String TAG = "ElectronBeam";
+
+ private static final boolean DEBUG = false;
+
+ // The layer for the electron beam surface.
+ // This is currently hardcoded to be one layer above the boot animation.
+ private static final int ELECTRON_BEAM_LAYER = 0x40000001;
+
+ // The relative proportion of the animation to spend performing
+ // the horizontal stretch effect. The remainder is spent performing
+ // the vertical stretch effect.
+ private static final float HSTRETCH_DURATION = 0.4f;
+ private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
+
+ // Set to true when the animation context has been fully prepared.
+ private boolean mPrepared;
+ private boolean mWarmUp;
+
+ private final Display mDisplay;
+ private final DisplayInfo mDisplayInfo = new DisplayInfo();
+ private int mDisplayLayerStack; // layer stack associated with primary display
+ private int mDisplayRotation;
+ private int mDisplayWidth; // real width, not rotated
+ private int mDisplayHeight; // real height, not rotated
+ private SurfaceSession mSurfaceSession;
+ private Surface mSurface;
+ private EGLDisplay mEglDisplay;
+ private EGLConfig mEglConfig;
+ private EGLContext mEglContext;
+ private EGLSurface mEglSurface;
+ private boolean mSurfaceVisible;
+
+ // Texture names. We only use one texture, which contains the screenshot.
+ private final int[] mTexNames = new int[1];
+ private boolean mTexNamesGenerated;
+
+ // Vertex and corresponding texture coordinates.
+ // We have 4 2D vertices, so 8 elements. The vertices form a quad.
+ private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
+ private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
+
+ public ElectronBeam(Display display) {
+ mDisplay = display;
+ }
+
+ /**
+ * Warms up the electron beam in preparation for turning on or off.
+ * This method prepares a GL context, and captures a screen shot.
+ *
+ * @param warmUp True if the electron beam is about to be turned on, false if
+ * it is about to be turned off.
+ * @return True if the electron beam is ready, false if it is uncontrollable.
+ */
+ public boolean prepare(boolean warmUp) {
+ if (DEBUG) {
+ Slog.d(TAG, "prepare: warmUp=" + warmUp);
+ }
+
+ mWarmUp = warmUp;
+
+ // Get the display size and adjust it for rotation.
+ mDisplay.getDisplayInfo(mDisplayInfo);
+ mDisplayLayerStack = mDisplay.getLayerStack();
+ mDisplayRotation = mDisplayInfo.rotation;
+ if (mDisplayRotation == Surface.ROTATION_90
+ || mDisplayRotation == Surface.ROTATION_270) {
+ mDisplayWidth = mDisplayInfo.logicalHeight;
+ mDisplayHeight = mDisplayInfo.logicalWidth;
+ } else {
+ mDisplayWidth = mDisplayInfo.logicalWidth;
+ mDisplayHeight = mDisplayInfo.logicalHeight;
+ }
+
+ // Prepare the surface for drawing.
+ if (!createEglContext()
+ || !createEglSurface()
+ || !captureScreenshotTextureAndSetViewport()) {
+ dismiss();
+ return false;
+ }
+
+ mPrepared = true;
+ return true;
+ }
+
+ /**
+ * Dismisses the electron beam animation surface and cleans up.
+ *
+ * To prevent stray photons from leaking out after the electron beam has been
+ * turned off, it is a good idea to defer dismissing the animation until the
+ * electron beam has been turned back on fully.
+ */
+ public void dismiss() {
+ if (DEBUG) {
+ Slog.d(TAG, "dismiss");
+ }
+
+ destroyScreenshotTexture();
+ destroyEglSurface();
+ mPrepared = false;
+ }
+
+ /**
+ * Draws an animation frame showing the electron beam activated at the
+ * specified level.
+ *
+ * @param level The electron beam level.
+ * @return True if successful.
+ */
+ public boolean draw(float level) {
+ if (DEBUG) {
+ Slog.d(TAG, "drawFrame: level=" + level);
+ }
+
+ if (!attachEglContext()) {
+ return false;
+ }
+ try {
+ // Clear frame to solid black.
+ GLES10.glClearColor(0f, 0f, 0f, 1f);
+ GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT);
+
+ // Draw the frame.
+ if (level < HSTRETCH_DURATION) {
+ drawHStretch(1.0f - (level / HSTRETCH_DURATION));
+ } else {
+ drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION));
+ }
+ if (checkGlErrors("drawFrame")) {
+ return false;
+ }
+
+ EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
+ } finally {
+ detachEglContext();
+ }
+
+ return showEglSurface();
+ }
+
+ /**
+ * Draws a frame where the content of the electron beam is collapsing inwards upon
+ * itself vertically with red / green / blue channels dispersing and eventually
+ * merging down to a single horizontal line.
+ *
+ * @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse.
+ */
+ private void drawVStretch(float stretch) {
+ // compute interpolation scale factors for each color channel
+ final float ar = scurve(stretch, 7.5f);
+ final float ag = scurve(stretch, 8.0f);
+ final float ab = scurve(stretch, 8.5f);
+ if (DEBUG) {
+ Slog.d(TAG, "drawVStretch: stretch=" + stretch
+ + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab);
+ }
+
+ // set blending
+ GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE);
+ GLES10.glEnable(GLES10.GL_BLEND);
+
+ // bind vertex buffer
+ GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
+ GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
+
+ // bind texture and set blending for drawing planes
+ GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
+ GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
+ mWarmUp ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
+ GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+ GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
+ GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+ GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
+ GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+ GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
+ GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+ GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);
+ GLES10.glEnable(GLES10.GL_TEXTURE_2D);
+ GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer);
+ GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
+
+ // draw the red plane
+ setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar);
+ GLES10.glColorMask(true, false, false, true);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // draw the green plane
+ setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
+ GLES10.glColorMask(false, true, false, true);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // draw the blue plane
+ setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab);
+ GLES10.glColorMask(false, false, true, true);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // clean up after drawing planes
+ GLES10.glDisable(GLES10.GL_TEXTURE_2D);
+ GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
+ GLES10.glColorMask(true, true, true, true);
+
+ // draw the white highlight (we use the last vertices)
+ if (!mWarmUp) {
+ GLES10.glColor4f(ag, ag, ag, 1.0f);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+ }
+
+ // clean up
+ GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
+ GLES10.glDisable(GLES10.GL_BLEND);
+ }
+
+ /**
+ * Draws a frame where the electron beam has been stretched out into
+ * a thin white horizontal line that fades as it expands outwards.
+ *
+ * @param stretch The stretch factor. 0.0 is no stretch / no fade,
+ * 1.0 is maximum stretch / maximum fade.
+ */
+ private void drawHStretch(float stretch) {
+ // compute interpolation scale factor
+ final float ag = scurve(stretch, 8.0f);
+ if (DEBUG) {
+ Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag);
+ }
+
+ if (stretch < 1.0f) {
+ // bind vertex buffer
+ GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
+ GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
+
+ // draw narrow fading white line
+ setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
+ GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // clean up
+ GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
+ }
+ }
+
+ private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
+ final float w = dw + (dw * a);
+ final float h = dh - (dh * a);
+ final float x = (dw - w) * 0.5f;
+ final float y = (dh - h) * 0.5f;
+ setQuad(vtx, x, y, w, h);
+ }
+
+ private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
+ final float w = dw + (dw * a);
+ final float h = 1.0f;
+ final float x = (dw - w) * 0.5f;
+ final float y = (dh - h) * 0.5f;
+ setQuad(vtx, x, y, w, h);
+ }
+
+ private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
+ if (DEBUG) {
+ Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
+ }
+ vtx.put(0, x);
+ vtx.put(1, y);
+ vtx.put(2, x);
+ vtx.put(3, y + h);
+ vtx.put(4, x + w);
+ vtx.put(5, y + h);
+ vtx.put(6, x + w);
+ vtx.put(7, y);
+ }
+
+ private boolean captureScreenshotTextureAndSetViewport() {
+ // TODO: Use a SurfaceTexture to avoid the extra texture upload.
+ Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight,
+ 0, ELECTRON_BEAM_LAYER - 1);
+ if (bitmap == null) {
+ Slog.e(TAG, "Could not take a screenshot!");
+ return false;
+ }
+ try {
+ if (!attachEglContext()) {
+ return false;
+ }
+ try {
+ if (!mTexNamesGenerated) {
+ GLES10.glGenTextures(1, mTexNames, 0);
+ if (checkGlErrors("glGenTextures")) {
+ return false;
+ }
+ mTexNamesGenerated = true;
+ }
+
+ GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
+ if (checkGlErrors("glBindTexture")) {
+ return false;
+ }
+
+ float u = 1.0f;
+ float v = 1.0f;
+ GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0);
+ if (checkGlErrors("glTexImage2D, first try", false)) {
+ // Try a power of two size texture instead.
+ int tw = nextPowerOfTwo(mDisplayWidth);
+ int th = nextPowerOfTwo(mDisplayHeight);
+ int format = GLUtils.getInternalFormat(bitmap);
+ GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0,
+ format, tw, th, 0,
+ format, GLES10.GL_UNSIGNED_BYTE, null);
+ if (checkGlErrors("glTexImage2D, second try")) {
+ return false;
+ }
+
+ GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap);
+ if (checkGlErrors("glTexSubImage2D")) {
+ return false;
+ }
+
+ u = (float)mDisplayWidth / tw;
+ v = (float)mDisplayHeight / th;
+ }
+
+ // Set up texture coordinates for a quad.
+ // We might need to change this if the texture ends up being
+ // a different size from the display for some reason.
+ mTexCoordBuffer.put(0, 0f);
+ mTexCoordBuffer.put(1, v);
+ mTexCoordBuffer.put(2, 0f);
+ mTexCoordBuffer.put(3, 0f);
+ mTexCoordBuffer.put(4, u);
+ mTexCoordBuffer.put(5, 0f);
+ mTexCoordBuffer.put(6, u);
+ mTexCoordBuffer.put(7, v);
+
+ // Set up our viewport.
+ GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
+ GLES10.glMatrixMode(GLES10.GL_PROJECTION);
+ GLES10.glLoadIdentity();
+ GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
+ GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
+ GLES10.glLoadIdentity();
+ GLES10.glMatrixMode(GLES10.GL_TEXTURE);
+ GLES10.glLoadIdentity();
+ } finally {
+ detachEglContext();
+ }
+ } finally {
+ bitmap.recycle();
+ }
+ return true;
+ }
+
+ private void destroyScreenshotTexture() {
+ if (mTexNamesGenerated) {
+ mTexNamesGenerated = false;
+ if (attachEglContext()) {
+ try {
+ GLES10.glDeleteTextures(1, mTexNames, 0);
+ checkGlErrors("glDeleteTextures");
+ } finally {
+ detachEglContext();
+ }
+ }
+ }
+ }
+
+ private boolean createEglContext() {
+ if (mEglDisplay == null) {
+ mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
+ logEglError("eglGetDisplay");
+ return false;
+ }
+
+ int[] version = new int[2];
+ if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
+ mEglDisplay = null;
+ logEglError("eglInitialize");
+ return false;
+ }
+ }
+
+ if (mEglConfig == null) {
+ int[] eglConfigAttribList = new int[] {
+ EGL14.EGL_RED_SIZE, 8,
+ EGL14.EGL_GREEN_SIZE, 8,
+ EGL14.EGL_BLUE_SIZE, 8,
+ EGL14.EGL_ALPHA_SIZE, 8,
+ EGL14.EGL_NONE
+ };
+ int[] numEglConfigs = new int[1];
+ EGLConfig[] eglConfigs = new EGLConfig[1];
+ if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
+ eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
+ logEglError("eglChooseConfig");
+ return false;
+ }
+ mEglConfig = eglConfigs[0];
+ }
+
+ if (mEglContext == null) {
+ int[] eglContextAttribList = new int[] {
+ EGL14.EGL_NONE
+ };
+ mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
+ EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
+ if (mEglContext == null) {
+ logEglError("eglCreateContext");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* not used because it is too expensive to create / destroy contexts all of the time
+ private void destroyEglContext() {
+ if (mEglContext != null) {
+ if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) {
+ logEglError("eglDestroyContext");
+ }
+ mEglContext = null;
+ }
+ }*/
+
+ private boolean createEglSurface() {
+ if (mSurfaceSession == null) {
+ mSurfaceSession = new SurfaceSession();
+ }
+
+ Surface.openTransaction();
+ try {
+ if (mSurface == null) {
+ try {
+ mSurface = new Surface(mSurfaceSession, Process.myPid(),
+ "ElectronBeam", mDisplayLayerStack, mDisplayWidth, mDisplayHeight,
+ PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN);
+ } catch (Surface.OutOfResourcesException ex) {
+ Slog.e(TAG, "Unable to create surface.", ex);
+ return false;
+ }
+ }
+
+ mSurface.setSize(mDisplayWidth, mDisplayHeight);
+
+ switch (mDisplayRotation) {
+ case Surface.ROTATION_0:
+ mSurface.setPosition(0, 0);
+ mSurface.setMatrix(1, 0, 0, 1);
+ break;
+ case Surface.ROTATION_90:
+ mSurface.setPosition(0, mDisplayWidth);
+ mSurface.setMatrix(0, -1, 1, 0);
+ break;
+ case Surface.ROTATION_180:
+ mSurface.setPosition(mDisplayWidth, mDisplayHeight);
+ mSurface.setMatrix(-1, 0, 0, -1);
+ break;
+ case Surface.ROTATION_270:
+ mSurface.setPosition(mDisplayHeight, 0);
+ mSurface.setMatrix(0, 1, -1, 0);
+ break;
+ }
+ } finally {
+ Surface.closeTransaction();
+ }
+
+ if (mEglSurface == null) {
+ int[] eglSurfaceAttribList = new int[] {
+ EGL14.EGL_NONE
+ };
+ mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
+ eglSurfaceAttribList, 0);
+ if (mEglSurface == null) {
+ logEglError("eglCreateWindowSurface");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void destroyEglSurface() {
+ if (mEglSurface != null) {
+ if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
+ logEglError("eglDestroySurface");
+ }
+ mEglSurface = null;
+ }
+
+ if (mSurface != null) {
+ Surface.openTransaction();
+ try {
+ mSurface.destroy();
+ } finally {
+ Surface.closeTransaction();
+ }
+ mSurface = null;
+ mSurfaceVisible = false;
+ }
+ }
+
+ private boolean showEglSurface() {
+ if (!mSurfaceVisible) {
+ Surface.openTransaction();
+ try {
+ mSurface.setLayer(ELECTRON_BEAM_LAYER);
+ mSurface.show();
+ } finally {
+ Surface.closeTransaction();
+ }
+ mSurfaceVisible = true;
+ }
+ return true;
+ }
+
+ private boolean attachEglContext() {
+ if (mEglSurface == null) {
+ return false;
+ }
+ if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ logEglError("eglMakeCurrent");
+ return false;
+ }
+ return true;
+ }
+
+ private void detachEglContext() {
+ if (mEglDisplay != null) {
+ EGL14.eglMakeCurrent(mEglDisplay,
+ EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
+ }
+ }
+
+ /**
+ * Interpolates a value in the range 0 .. 1 along a sigmoid curve
+ * yielding a result in the range 0 .. 1 scaled such that:
+ * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1.
+ */
+ private static float scurve(float value, float s) {
+ // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s).
+ // Here we take the input datum and shift it by 0.5 so that the
+ // domain spans the range -0.5 .. 0.5 instead of 0 .. 1.
+ final float x = value - 0.5f;
+
+ // Next apply the sigmoid function to the scaled value
+ // which produces a value in the range 0 .. 1 so we subtract
+ // 0.5 to get a value in the range -0.5 .. 0.5 instead.
+ final float y = sigmoid(x, s) - 0.5f;
+
+ // To obtain the desired boundary conditions we need to scale
+ // the result so that it fills a range of -1 .. 1.
+ final float v = sigmoid(0.5f, s) - 0.5f;
+
+ // And finally remap the value back to a range of 0 .. 1.
+ return y / v * 0.5f + 0.5f;
+ }
+
+ private static float sigmoid(float x, float s) {
+ return 1.0f / (1.0f + FloatMath.exp(-x * s));
+ }
+
+ private static int nextPowerOfTwo(int value) {
+ return 1 << (32 - Integer.numberOfLeadingZeros(value));
+ }
+
+ private static FloatBuffer createNativeFloatBuffer(int size) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
+ bb.order(ByteOrder.nativeOrder());
+ return bb.asFloatBuffer();
+ }
+
+ private static void logEglError(String func) {
+ Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
+ }
+
+ private static boolean checkGlErrors(String func) {
+ return checkGlErrors(func, true);
+ }
+
+ private static boolean checkGlErrors(String func, boolean log) {
+ boolean hadError = false;
+ int error;
+ while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) {
+ if (log) {
+ Slog.e(TAG, func + " failed: error " + error, new Throwable());
+ }
+ hadError = true;
+ }
+ return hadError;
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("Electron Beam State:");
+ pw.println(" mPrepared=" + mPrepared);
+ pw.println(" mWarmUp=" + mWarmUp);
+ pw.println(" mDisplayLayerStack=" + mDisplayLayerStack);
+ pw.println(" mDisplayRotation=" + mDisplayRotation);
+ pw.println(" mDisplayWidth=" + mDisplayWidth);
+ pw.println(" mDisplayHeight=" + mDisplayHeight);
+ pw.println(" mSurfaceVisible=" + mSurfaceVisible);
+ }
+}
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
new file mode 100644
index 0000000..75f8445
--- /dev/null
+++ b/services/java/com/android/server/power/Notifier.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.server.EventLogTags;
+
+import android.app.ActivityManagerNative;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.EventLog;
+import android.util.Slog;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManagerPolicy.ScreenOnListener;
+
+/**
+ * Sends broadcasts about important power state changes.
+ *
+ * This methods of this class may be called by the power manager service while
+ * its lock is being held. Internally it takes care of sending broadcasts to
+ * notify other components of the system or applications asynchronously.
+ *
+ * The notifier is designed to collapse unnecessary broadcasts when it is not
+ * possible for the system to have observed an intermediate state.
+ *
+ * For example, if the device wakes up, goes to sleep and wakes up again immediately
+ * before the go to sleep broadcast has been sent, then no broadcast will be
+ * sent about the system going to sleep and waking up.
+ */
+final class Notifier {
+ private static final String TAG = "PowerManagerNotifier";
+
+ private static final boolean DEBUG = false;
+
+ private static final int POWER_STATE_UNKNOWN = 0;
+ private static final int POWER_STATE_AWAKE = 1;
+ private static final int POWER_STATE_ASLEEP = 2;
+
+ private static final int MSG_USER_ACTIVITY = 1;
+ private static final int MSG_BROADCAST = 2;
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+ private final IBatteryStats mBatteryStats;
+ private final SuspendBlocker mSuspendBlocker;
+ private final WindowManagerPolicy mPolicy;
+ private final ScreenOnListener mScreenOnListener;
+
+ private final NotifierHandler mHandler;
+ private final Intent mScreenOnIntent;
+ private final Intent mScreenOffIntent;
+
+ // The current power state.
+ private int mActualPowerState;
+ private int mLastGoToSleepReason;
+
+ // The currently broadcasted power state. This reflects what other parts of the
+ // system have observed.
+ private int mBroadcastedPowerState;
+ private boolean mBroadcastInProgress;
+ private long mBroadcastStartTime;
+
+ // True if a user activity message should be sent.
+ private boolean mUserActivityPending;
+
+ public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
+ SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+ ScreenOnListener screenOnListener) {
+ mContext = context;
+ mBatteryStats = batteryStats;
+ mSuspendBlocker = suspendBlocker;
+ mPolicy = policy;
+ mScreenOnListener = screenOnListener;
+
+ mHandler = new NotifierHandler(looper);
+ mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
+ mScreenOnIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+ mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
+ mScreenOffIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+ }
+
+ /**
+ * Called when a wake lock is acquired.
+ */
+ public void onWakeLockAcquired(int flags, String tag, int ownerUid, int ownerPid,
+ WorkSource workSource) {
+ if (DEBUG) {
+ Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
+ + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ + ", workSource=" + workSource);
+ }
+
+ try {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (workSource != null) {
+ mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, monitorType);
+ } else {
+ mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, monitorType);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+
+ /**
+ * Called when a wake lock is released.
+ */
+ public void onWakeLockReleased(int flags, String tag, int ownerUid, int ownerPid,
+ WorkSource workSource) {
+ if (DEBUG) {
+ Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
+ + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ + ", workSource=" + workSource);
+ }
+
+ try {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (workSource != null) {
+ mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+
+ private static int getBatteryStatsWakeLockMonitorType(int flags) {
+ switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ return BatteryStats.WAKE_TYPE_PARTIAL;
+ default:
+ return BatteryStats.WAKE_TYPE_FULL;
+ }
+ }
+
+ /**
+ * Called when the screen is turned on.
+ */
+ public void onScreenOn() {
+ if (DEBUG) {
+ Slog.d(TAG, "onScreenOn");
+ }
+
+ try {
+ mBatteryStats.noteScreenOn();
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+
+ /**
+ * Called when the screen is turned off.
+ */
+ public void onScreenOff() {
+ if (DEBUG) {
+ Slog.d(TAG, "onScreenOff");
+ }
+
+ try {
+ mBatteryStats.noteScreenOff();
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+
+ /**
+ * Called when the screen changes brightness.
+ */
+ public void onScreenBrightness(int brightness) {
+ if (DEBUG) {
+ Slog.d(TAG, "onScreenBrightness: brightness=" + brightness);
+ }
+
+ try {
+ mBatteryStats.noteScreenBrightness(brightness);
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+
+ /**
+ * Called when the device is waking up from sleep and the
+ * display is about to be turned on.
+ */
+ public void onWakeUpStarted() {
+ if (DEBUG) {
+ Slog.d(TAG, "onWakeUpStarted");
+ }
+
+ synchronized (mLock) {
+ if (mActualPowerState != POWER_STATE_AWAKE) {
+ mActualPowerState = POWER_STATE_AWAKE;
+ updatePendingBroadcastLocked();
+ }
+ }
+ }
+
+ /**
+ * Called when the device has finished waking up from sleep
+ * and the display has been turned on.
+ */
+ public void onWakeUpFinished() {
+ if (DEBUG) {
+ Slog.d(TAG, "onWakeUpFinished");
+ }
+ }
+
+ /**
+ * Called when the device is going to sleep.
+ */
+ public void onGoToSleepStarted(int reason) {
+ if (DEBUG) {
+ Slog.d(TAG, "onGoToSleepStarted");
+ }
+
+ synchronized (mLock) {
+ mLastGoToSleepReason = reason;
+ }
+ }
+
+ /**
+ * Called when the device has finished going to sleep and the
+ * display has been turned off.
+ *
+ * This is a good time to make transitions that we don't want the user to see,
+ * such as bringing the key guard to focus. There's no guarantee for this,
+ * however because the user could turn the device on again at any time.
+ * Some things may need to be protected by other mechanisms that defer screen on.
+ */
+ public void onGoToSleepFinished() {
+ if (DEBUG) {
+ Slog.d(TAG, "onGoToSleepFinished");
+ }
+
+ synchronized (mLock) {
+ if (mActualPowerState != POWER_STATE_ASLEEP) {
+ mActualPowerState = POWER_STATE_ASLEEP;
+ if (mUserActivityPending) {
+ mUserActivityPending = false;
+ mHandler.removeMessages(MSG_USER_ACTIVITY);
+ }
+ updatePendingBroadcastLocked();
+ }
+ }
+ }
+
+ /**
+ * Called when there has been user activity.
+ */
+ public void onUserActivity(int event, int uid) {
+ if (DEBUG) {
+ Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
+ }
+
+ try {
+ mBatteryStats.noteUserActivity(uid, event);
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+
+ synchronized (mLock) {
+ if (!mUserActivityPending) {
+ mUserActivityPending = true;
+ Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+ }
+ }
+
+ private void updatePendingBroadcastLocked() {
+ if (!mBroadcastInProgress
+ && mActualPowerState != POWER_STATE_UNKNOWN
+ && mActualPowerState != mBroadcastedPowerState) {
+ mBroadcastInProgress = true;
+ mSuspendBlocker.acquire();
+ Message msg = mHandler.obtainMessage(MSG_BROADCAST);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private void sendUserActivity() {
+ synchronized (mLock) {
+ if (!mUserActivityPending) {
+ return;
+ }
+ mUserActivityPending = false;
+ }
+
+ mPolicy.userActivity();
+ }
+
+ private void sendNextBroadcast() {
+ final int powerState;
+ final int goToSleepReason;
+ synchronized (mLock) {
+ if (mActualPowerState == POWER_STATE_UNKNOWN
+ || mActualPowerState == mBroadcastedPowerState) {
+ mBroadcastInProgress = false;
+ mSuspendBlocker.release();
+ return;
+ }
+
+ powerState = mActualPowerState;
+ goToSleepReason = mLastGoToSleepReason;
+
+ mBroadcastedPowerState = powerState;
+ mBroadcastStartTime = SystemClock.uptimeMillis();
+ }
+
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
+
+ if (powerState == POWER_STATE_AWAKE) {
+ sendWakeUpBroadcast();
+ } else {
+ sendGoToSleepBroadcast(goToSleepReason);
+ }
+ }
+
+ private void sendWakeUpBroadcast() {
+ if (DEBUG) {
+ Slog.d(TAG, "Sending wake up broadcast.");
+ }
+
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
+
+ mPolicy.screenTurningOn(mScreenOnListener);
+ try {
+ ActivityManagerNative.getDefault().wakingUp();
+ } catch (RemoteException e) {
+ // ignore it
+ }
+
+ if (ActivityManagerNative.isSystemReady()) {
+ mContext.sendOrderedBroadcast(mScreenOnIntent, null,
+ mWakeUpBroadcastDone, mHandler, 0, null, null);
+ } else {
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
+ sendNextBroadcast();
+ }
+ }
+
+ private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
+ SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
+ sendNextBroadcast();
+ }
+ };
+
+ private void sendGoToSleepBroadcast(int reason) {
+ if (DEBUG) {
+ Slog.d(TAG, "Sending go to sleep broadcast.");
+ }
+
+ int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
+ switch (reason) {
+ case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
+ why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
+ break;
+ case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+ why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
+ break;
+ }
+
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
+
+ mPolicy.screenTurnedOff(why);
+ try {
+ ActivityManagerNative.getDefault().goingToSleep();
+ } catch (RemoteException e) {
+ // ignore it.
+ }
+
+ if (ActivityManagerNative.isSystemReady()) {
+ mContext.sendOrderedBroadcast(mScreenOffIntent, null,
+ mGoToSleepBroadcastDone, mHandler, 0, null, null);
+ } else {
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
+ sendNextBroadcast();
+ }
+ }
+
+ private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
+ SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
+ sendNextBroadcast();
+ }
+ };
+
+ private final class NotifierHandler extends Handler {
+ public NotifierHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_USER_ACTIVITY:
+ sendUserActivity();
+ break;
+
+ case MSG_BROADCAST:
+ sendNextBroadcast();
+ break;
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/power/PhotonicModulator.java b/services/java/com/android/server/power/PhotonicModulator.java
new file mode 100644
index 0000000..f7c9c7d
--- /dev/null
+++ b/services/java/com/android/server/power/PhotonicModulator.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import com.android.server.LightsService;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Sets the value of a light asynchronously.
+ *
+ * This is done to avoid blocking the looper on devices for which
+ * setting the backlight brightness is especially slow.
+ */
+final class PhotonicModulator {
+ private static final int UNKNOWN_LIGHT_VALUE = -1;
+
+ private final Object mLock = new Object();
+
+ private final LightsService.Light mLight;
+ private final Executor mExecutor;
+ private final SuspendBlocker mSuspendBlocker;
+
+ private boolean mPendingChange;
+ private int mPendingLightValue;
+ private int mActualLightValue;
+
+ public PhotonicModulator(Executor executor, LightsService.Light light,
+ SuspendBlocker suspendBlocker) {
+ mExecutor = executor;
+ mLight = light;
+ mSuspendBlocker = suspendBlocker;
+ mPendingLightValue = UNKNOWN_LIGHT_VALUE;
+ mActualLightValue = UNKNOWN_LIGHT_VALUE;
+ }
+
+ /**
+ * Asynchronously sets the backlight brightness.
+ *
+ * @param lightValue The new light value, from 0 to 255.
+ */
+ public void setBrightness(int lightValue) {
+ synchronized (mLock) {
+ if (lightValue != mPendingLightValue) {
+ mPendingLightValue = lightValue;
+ if (!mPendingChange) {
+ mPendingChange = true;
+ mSuspendBlocker.acquire();
+ mExecutor.execute(mTask);
+ }
+ }
+ }
+ }
+
+ private final Runnable mTask = new Runnable() {
+ @Override
+ public void run() {
+ for (;;) {
+ final int newLightValue;
+ synchronized (mLock) {
+ newLightValue = mPendingLightValue;
+ if (newLightValue == mActualLightValue) {
+ mSuspendBlocker.release();
+ mPendingChange = false;
+ return;
+ }
+ mActualLightValue = newLightValue;
+ }
+ mLight.setBrightness(newLightValue);
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
new file mode 100644
index 0000000..6d68104
--- /dev/null
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -0,0 +1,2121 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.server.BatteryService;
+import com.android.server.EventLogTags;
+import com.android.server.LightsService;
+import com.android.server.TwilightService;
+import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.display.DisplayManagerService;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.service.dreams.IDreamManager;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.view.WindowManagerPolicy;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import libcore.util.Objects;
+
+/**
+ * The power manager service is responsible for coordinating power management
+ * functions on the device.
+ */
+public final class PowerManagerService extends IPowerManager.Stub
+ implements Watchdog.Monitor {
+ private static final String TAG = "PowerManagerService";
+
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_SPEW = DEBUG && true;
+
+ // Message: Sent when a user activity timeout occurs to update the power state.
+ private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
+ // Message: Sent when the device enters or exits a napping or dreaming state.
+ private static final int MSG_SANDMAN = 2;
+
+ // Dirty bit: mWakeLocks changed
+ private static final int DIRTY_WAKE_LOCKS = 1 << 0;
+ // Dirty bit: mWakefulness changed
+ private static final int DIRTY_WAKEFULNESS = 1 << 1;
+ // Dirty bit: user activity was poked or may have timed out
+ private static final int DIRTY_USER_ACTIVITY = 1 << 2;
+ // Dirty bit: actual display power state was updated asynchronously
+ private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 << 3;
+ // Dirty bit: mBootCompleted changed
+ private static final int DIRTY_BOOT_COMPLETED = 1 << 4;
+ // Dirty bit: settings changed
+ private static final int DIRTY_SETTINGS = 1 << 5;
+ // Dirty bit: mIsPowered changed
+ private static final int DIRTY_IS_POWERED = 1 << 6;
+ // Dirty bit: mStayOn changed
+ private static final int DIRTY_STAY_ON = 1 << 7;
+ // Dirty bit: battery state changed
+ private static final int DIRTY_BATTERY_STATE = 1 << 8;
+
+ // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
+ // The screen should be off or in the process of being turned off by the display controller.
+ private static final int WAKEFULNESS_ASLEEP = 0;
+ // Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep().
+ // When the user activity timeout expires, the device may start napping.
+ private static final int WAKEFULNESS_AWAKE = 1;
+ // Wakefulness: The device is napping. It is deciding whether to dream or go to sleep
+ // but hasn't gotten around to it yet. It can be awoken by a call to wakeUp(), which
+ // ends the nap. User activity may brighten the screen but does not end the nap.
+ private static final int WAKEFULNESS_NAPPING = 2;
+ // Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(),
+ // which ends the dream. The device goes to sleep when goToSleep() is called, when
+ // the dream ends or when unplugged.
+ // User activity may brighten the screen but does not end the dream.
+ private static final int WAKEFULNESS_DREAMING = 3;
+
+ // Summarizes the state of all active wakelocks.
+ private static final int WAKE_LOCK_CPU = 1 << 0;
+ private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1;
+ private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2;
+ private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3;
+ private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;
+
+ // Summarizes the user activity state.
+ private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
+ private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1;
+
+ // Default and minimum screen off timeout in milliseconds.
+ private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
+ private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000;
+
+ // The screen dim duration, in seconds.
+ // This is subtracted from the end of the screen off timeout so the
+ // minimum screen off timeout should be longer than this.
+ private static final int SCREEN_DIM_DURATION = 7 * 1000;
+
+ private Context mContext;
+ private LightsService mLightsService;
+ private BatteryService mBatteryService;
+ private IBatteryStats mBatteryStats;
+ private HandlerThread mHandlerThread;
+ private PowerManagerHandler mHandler;
+ private WindowManagerPolicy mPolicy;
+ private Notifier mNotifier;
+ private DisplayPowerController mDisplayPowerController;
+ private SettingsObserver mSettingsObserver;
+ private IDreamManager mDreamManager;
+ private LightsService.Light mAttentionLight;
+
+ private final Object mLock = new Object();
+
+ // A bitfield that indicates what parts of the power state have
+ // changed and need to be recalculated.
+ private int mDirty;
+
+ // Indicates whether the device is awake or asleep or somewhere in between.
+ // This is distinct from the screen power state, which is managed separately.
+ private int mWakefulness;
+
+ // True if MSG_SANDMAN has been scheduled.
+ private boolean mSandmanScheduled;
+
+ // Table of all suspend blockers.
+ // There should only be a few of these.
+ private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>();
+
+ // Table of all wake locks acquired by applications.
+ private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>();
+
+ // A bitfield that summarizes the state of all active wakelocks.
+ private int mWakeLockSummary;
+
+ // If true, instructs the display controller to wait for the proximity sensor to
+ // go negative before turning the screen on.
+ private boolean mRequestWaitForNegativeProximity;
+
+ // Timestamp of the last time the device was awoken or put to sleep.
+ private long mLastWakeTime;
+ private long mLastSleepTime;
+
+ // True if we need to send a wake up or go to sleep finished notification
+ // when the display is ready.
+ private boolean mSendWakeUpFinishedNotificationWhenReady;
+ private boolean mSendGoToSleepFinishedNotificationWhenReady;
+
+ // Timestamp of the last call to user activity.
+ private long mLastUserActivityTime;
+ private long mLastUserActivityTimeNoChangeLights;
+
+ // A bitfield that summarizes the effect of the user activity timer.
+ // A zero value indicates that the user activity timer has expired.
+ private int mUserActivitySummary;
+
+ // The desired display power state. The actual state may lag behind the
+ // requested because it is updated asynchronously by the display power controller.
+ private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest();
+
+ // The time the screen was last turned off, in elapsedRealtime() timebase.
+ private long mLastScreenOffEventElapsedRealTime;
+
+ // True if the display power state has been fully applied, which means the display
+ // is actually on or actually off or whatever was requested.
+ private boolean mDisplayReady;
+
+ // True if holding a wake-lock to block suspend of the CPU.
+ private boolean mHoldingWakeLockSuspendBlocker;
+
+ // The suspend blocker used to keep the CPU alive when wake locks have been acquired.
+ private final SuspendBlocker mWakeLockSuspendBlocker;
+
+ // True if systemReady() has been called.
+ private boolean mSystemReady;
+
+ // True if boot completed occurred. We keep the screen on until this happens.
+ private boolean mBootCompleted;
+
+ // True if the device is plugged into a power source.
+ private boolean mIsPowered;
+
+ // True if the device should wake up when plugged or unplugged.
+ private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
+
+ // True if dreams are supported on this device.
+ private boolean mDreamsSupportedConfig;
+
+ // True if dreams are enabled by the user.
+ private boolean mDreamsEnabledSetting;
+
+ // True if dreams should be activated on sleep.
+ private boolean mDreamsActivateOnSleepSetting;
+
+ // The screen off timeout setting value in milliseconds.
+ private int mScreenOffTimeoutSetting;
+
+ // The maximum allowable screen off timeout according to the device
+ // administration policy. Overrides other settings.
+ private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE;
+
+ // The stay on while plugged in setting.
+ // A bitfield of battery conditions under which to make the screen stay on.
+ private int mStayOnWhilePluggedInSetting;
+
+ // True if the device should stay on.
+ private boolean mStayOn;
+
+ // Screen brightness setting limits.
+ private int mScreenBrightnessSettingMinimum;
+ private int mScreenBrightnessSettingMaximum;
+ private int mScreenBrightnessSettingDefault;
+
+ // The screen brightness setting, from 0 to 255.
+ // Use -1 if no value has been set.
+ private int mScreenBrightnessSetting;
+
+ // The screen auto-brightness adjustment setting, from -1 to 1.
+ // Use 0 if there is no adjustment.
+ private float mScreenAutoBrightnessAdjustmentSetting;
+
+ // The screen brightness mode.
+ // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants.
+ private int mScreenBrightnessModeSetting;
+
+ // The screen brightness setting override from the window manager
+ // to allow the current foreground activity to override the brightness.
+ // Use -1 to disable.
+ private int mScreenBrightnessOverrideFromWindowManager = -1;
+
+ // The screen brightness setting override from the settings application
+ // to temporarily adjust the brightness until next updated,
+ // Use -1 to disable.
+ private int mTemporaryScreenBrightnessSettingOverride = -1;
+
+ // The screen brightness adjustment setting override from the settings
+ // application to temporarily adjust the auto-brightness adjustment factor
+ // until next updated, in the range -1..1.
+ // Use NaN to disable.
+ private float mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
+
+ private native void nativeInit();
+ private static native void nativeShutdown();
+ private static native void nativeReboot(String reason) throws IOException;
+
+ private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
+ private static native void nativeAcquireSuspendBlocker(String name);
+ private static native void nativeReleaseSuspendBlocker(String name);
+
+ static native void nativeSetScreenState(boolean on);
+
+ public PowerManagerService() {
+ synchronized (mLock) {
+ mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService");
+ mWakeLockSuspendBlocker.acquire();
+ mHoldingWakeLockSuspendBlocker = true;
+ mWakefulness = WAKEFULNESS_AWAKE;
+ }
+
+ nativeInit();
+ nativeSetPowerState(true, true);
+ }
+
+ /**
+ * Initialize the power manager.
+ * Must be called before any other functions within the power manager are called.
+ */
+ public void init(Context context, LightsService ls,
+ ActivityManagerService am, BatteryService bs, IBatteryStats bss,
+ DisplayManagerService dm) {
+ // Forcibly turn the screen on at boot so that it is in a known power state.
+ // We do this in init() rather than in the constructor because setting the
+ // screen state requires a call into surface flinger which then needs to call back
+ // into the activity manager to check permissions. Unfortunately the
+ // activity manager is not running when the constructor is called, so we
+ // have to defer setting the screen state until this point.
+ nativeSetScreenState(true);
+
+ mContext = context;
+ mLightsService = ls;
+ mBatteryService = bs;
+ mBatteryStats = bss;
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+
+ Watchdog.getInstance().addMonitor(this);
+ }
+
+ public void setPolicy(WindowManagerPolicy policy) {
+ synchronized (mLock) {
+ mPolicy = policy;
+ }
+ }
+
+ public void systemReady(TwilightService twilight) {
+ synchronized (mLock) {
+ mSystemReady = true;
+
+ PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
+ mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
+ mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
+
+ mNotifier = new Notifier(mHandler.getLooper(), mContext, mBatteryStats,
+ createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
+ mPolicy, mScreenOnListener);
+ mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
+ mContext, mNotifier, mLightsService, twilight,
+ createSuspendBlockerLocked("PowerManagerService.Display"),
+ mDisplayPowerControllerCallbacks, mHandler);
+
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION);
+
+ // Register for broadcasts from other components of the system.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ mContext.registerReceiver(new BatteryReceiver(), filter);
+
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ mContext.registerReceiver(new BootCompletedReceiver(), filter);
+
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+ mContext.registerReceiver(new DockReceiver(), filter);
+
+ // Register for settings changes.
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SCREENSAVER_ENABLED), false, mSettingsObserver);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP), false, mSettingsObserver);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.SCREEN_OFF_TIMEOUT), false, mSettingsObserver);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.STAY_ON_WHILE_PLUGGED_IN), false, mSettingsObserver);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS), false, mSettingsObserver);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS_MODE), false, mSettingsObserver);
+
+ // Go.
+ readConfigurationLocked();
+ updateSettingsLocked();
+ mDirty |= DIRTY_BATTERY_STATE;
+ updatePowerStateLocked();
+ }
+ }
+
+ private void readConfigurationLocked() {
+ final Resources resources = mContext.getResources();
+
+ mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_unplugTurnsOnScreen);
+ mDreamsSupportedConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_enableDreams);
+ }
+
+ private void updateSettingsLocked() {
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ mDreamsEnabledSetting = (Settings.Secure.getInt(resolver,
+ Settings.Secure.SCREENSAVER_ENABLED, 0) != 0);
+ mDreamsActivateOnSleepSetting = (Settings.Secure.getInt(resolver,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 0) != 0);
+ mScreenOffTimeoutSetting = Settings.System.getInt(resolver,
+ Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
+ mStayOnWhilePluggedInSetting = Settings.System.getInt(resolver,
+ Settings.System.STAY_ON_WHILE_PLUGGED_IN,
+ BatteryManager.BATTERY_PLUGGED_AC);
+
+ final int oldScreenBrightnessSetting = mScreenBrightnessSetting;
+ mScreenBrightnessSetting = Settings.System.getInt(resolver,
+ Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault);
+ if (oldScreenBrightnessSetting != mScreenBrightnessSetting) {
+ mTemporaryScreenBrightnessSettingOverride = -1;
+ }
+
+ final float oldScreenAutoBrightnessAdjustmentSetting =
+ mScreenAutoBrightnessAdjustmentSetting;
+ mScreenAutoBrightnessAdjustmentSetting = Settings.System.getFloat(resolver,
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f);
+ if (oldScreenAutoBrightnessAdjustmentSetting != mScreenAutoBrightnessAdjustmentSetting) {
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
+ }
+
+ mScreenBrightnessModeSetting = Settings.System.getInt(resolver,
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+
+ mDirty |= DIRTY_SETTINGS;
+ }
+
+ private void handleSettingsChangedLocked() {
+ updateSettingsLocked();
+ updatePowerStateLocked();
+ }
+
+ @Override // Binder call
+ public void acquireWakeLock(IBinder lock, int flags, String tag, WorkSource ws) {
+ if (lock == null) {
+ throw new IllegalArgumentException("lock must not be null");
+ }
+ PowerManager.validateWakeLockParameters(flags, tag);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+ if (ws != null && ws.size() != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_STATS, null);
+ } else {
+ ws = null;
+ }
+
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ acquireWakeLockInternal(lock, flags, tag, ws, uid, pid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws,
+ int uid, int pid) {
+ synchronized (mLock) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock)
+ + ", flags=0x" + Integer.toHexString(flags)
+ + ", tag=\"" + tag + "\", ws=" + ws + ", uid=" + uid + ", pid=" + pid);
+ }
+
+ WakeLock wakeLock;
+ int index = findWakeLockIndexLocked(lock);
+ if (index >= 0) {
+ wakeLock = mWakeLocks.get(index);
+ if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
+ // Update existing wake lock. This shouldn't happen but is harmless.
+ notifyWakeLockReleasedLocked(wakeLock);
+ wakeLock.updateProperties(flags, tag, ws, uid, pid);
+ notifyWakeLockAcquiredLocked(wakeLock);
+ }
+ } else {
+ wakeLock = new WakeLock(lock, flags, tag, ws, uid, pid);
+ try {
+ lock.linkToDeath(wakeLock, 0);
+ } catch (RemoteException ex) {
+ throw new IllegalArgumentException("Wake lock is already dead.");
+ }
+ notifyWakeLockAcquiredLocked(wakeLock);
+ mWakeLocks.add(wakeLock);
+ }
+
+ applyWakeLockFlagsOnAcquireLocked(wakeLock);
+ mDirty |= DIRTY_WAKE_LOCKS;
+ updatePowerStateLocked();
+ }
+ }
+
+ private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock) {
+ if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
+ wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
+ }
+ }
+
+ @Override // Binder call
+ public void releaseWakeLock(IBinder lock, int flags) {
+ if (lock == null) {
+ throw new IllegalArgumentException("lock must not be null");
+ }
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ releaseWakeLockInternal(lock, flags);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void releaseWakeLockInternal(IBinder lock, int flags) {
+ synchronized (mLock) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock)
+ + ", flags=0x" + Integer.toHexString(flags));
+ }
+
+ int index = findWakeLockIndexLocked(lock);
+ if (index < 0) {
+ return;
+ }
+
+ WakeLock wakeLock = mWakeLocks.get(index);
+ mWakeLocks.remove(index);
+ notifyWakeLockReleasedLocked(wakeLock);
+ wakeLock.mLock.unlinkToDeath(wakeLock, 0);
+
+ if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) {
+ mRequestWaitForNegativeProximity = true;
+ }
+
+ applyWakeLockFlagsOnReleaseLocked(wakeLock);
+ mDirty |= DIRTY_WAKE_LOCKS;
+ updatePowerStateLocked();
+ }
+ }
+
+ private void handleWakeLockDeath(WakeLock wakeLock) {
+ synchronized (mLock) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "handleWakeLockDeath: lock=" + Objects.hashCode(wakeLock.mLock));
+ }
+
+ int index = mWakeLocks.indexOf(wakeLock);
+ if (index < 0) {
+ return;
+ }
+
+ mWakeLocks.remove(index);
+ notifyWakeLockReleasedLocked(wakeLock);
+
+ applyWakeLockFlagsOnReleaseLocked(wakeLock);
+ mDirty |= DIRTY_WAKE_LOCKS;
+ updatePowerStateLocked();
+ }
+ }
+
+ private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
+ if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
+ userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
+ wakeLock.mOwnerUid);
+ }
+ }
+
+ @Override // Binder call
+ public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
+ if (lock == null) {
+ throw new IllegalArgumentException("lock must not be null");
+ }
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+ if (ws != null && ws.size() != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_STATS, null);
+ } else {
+ ws = null;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ updateWakeLockWorkSourceInternal(lock, ws);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws) {
+ synchronized (mLock) {
+ int index = findWakeLockIndexLocked(lock);
+ if (index < 0) {
+ throw new IllegalArgumentException("Wake lock not active");
+ }
+
+ WakeLock wakeLock = mWakeLocks.get(index);
+ if (!wakeLock.hasSameWorkSource(ws)) {
+ notifyWakeLockReleasedLocked(wakeLock);
+ wakeLock.updateWorkSource(ws);
+ notifyWakeLockAcquiredLocked(wakeLock);
+ }
+ }
+ }
+
+ private int findWakeLockIndexLocked(IBinder lock) {
+ final int count = mWakeLocks.size();
+ for (int i = 0; i < count; i++) {
+ if (mWakeLocks.get(i).mLock == lock) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
+ if (mSystemReady) {
+ mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag,
+ wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
+ }
+ }
+
+ private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
+ if (mSystemReady) {
+ mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
+ wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
+ }
+ }
+
+ @Override // Binder call
+ public boolean isWakeLockLevelSupported(int level) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isWakeLockLevelSupportedInternal(level);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean isWakeLockLevelSupportedInternal(int level) {
+ synchronized (mLock) {
+ switch (level) {
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ case PowerManager.SCREEN_DIM_WAKE_LOCK:
+ case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ case PowerManager.FULL_WAKE_LOCK:
+ return true;
+
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ return mSystemReady && mDisplayPowerController.isProximitySensorAvailable();
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ @Override // Binder call
+ public void userActivity(long eventTime, int event, int flags) {
+ if (eventTime > SystemClock.uptimeMillis()) {
+ throw new IllegalArgumentException("event time must not be in the future");
+ }
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ userActivityInternal(eventTime, event, flags, uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Called from native code.
+ private void userActivityFromNative(long eventTime, int event, int flags) {
+ userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
+ }
+
+ private void userActivityInternal(long eventTime, int event, int flags, int uid) {
+ synchronized (mLock) {
+ if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
+ updatePowerStateLocked();
+ }
+ }
+ }
+
+ private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime
+ + ", event=" + event + ", flags=0x" + Integer.toHexString(flags)
+ + ", uid=" + uid);
+ }
+
+ if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
+ || mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) {
+ return false;
+ }
+
+ mNotifier.onUserActivity(event, uid);
+
+ if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
+ if (eventTime > mLastUserActivityTimeNoChangeLights
+ && eventTime > mLastUserActivityTime) {
+ mLastUserActivityTimeNoChangeLights = eventTime;
+ mDirty |= DIRTY_USER_ACTIVITY;
+ return true;
+ }
+ } else {
+ if (eventTime > mLastUserActivityTime) {
+ mLastUserActivityTime = eventTime;
+ mDirty |= DIRTY_USER_ACTIVITY;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override // Binder call
+ public void wakeUp(long eventTime) {
+ if (eventTime > SystemClock.uptimeMillis()) {
+ throw new IllegalArgumentException("event time must not be in the future");
+ }
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ wakeUpInternal(eventTime);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Called from native code.
+ private void wakeUpFromNative(long eventTime) {
+ wakeUpInternal(eventTime);
+ }
+
+ private void wakeUpInternal(long eventTime) {
+ synchronized (mLock) {
+ if (wakeUpNoUpdateLocked(eventTime)) {
+ updatePowerStateLocked();
+ }
+ }
+ }
+
+ private boolean wakeUpNoUpdateLocked(long eventTime) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime);
+ }
+
+ if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
+ || !mBootCompleted || !mSystemReady) {
+ return false;
+ }
+
+ switch (mWakefulness) {
+ case WAKEFULNESS_ASLEEP:
+ Slog.i(TAG, "Waking up from sleep...");
+ mNotifier.onWakeUpStarted();
+ mSendWakeUpFinishedNotificationWhenReady = true;
+ mSendGoToSleepFinishedNotificationWhenReady = false;
+ break;
+ case WAKEFULNESS_DREAMING:
+ Slog.i(TAG, "Waking up from dream...");
+ break;
+ case WAKEFULNESS_NAPPING:
+ Slog.i(TAG, "Waking up from nap...");
+ break;
+ }
+
+ mLastWakeTime = eventTime;
+ mWakefulness = WAKEFULNESS_AWAKE;
+ mDirty |= DIRTY_WAKEFULNESS;
+
+ userActivityNoUpdateLocked(
+ eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+ return true;
+ }
+
+ @Override // Binder call
+ public void goToSleep(long eventTime, int reason) {
+ if (eventTime > SystemClock.uptimeMillis()) {
+ throw new IllegalArgumentException("event time must not be in the future");
+ }
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ goToSleepInternal(eventTime, reason);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Called from native code.
+ private void goToSleepFromNative(long eventTime, int reason) {
+ goToSleepInternal(eventTime, reason);
+ }
+
+ private void goToSleepInternal(long eventTime, int reason) {
+ synchronized (mLock) {
+ if (goToSleepNoUpdateLocked(eventTime, reason)) {
+ updatePowerStateLocked();
+ }
+ }
+ }
+
+ private boolean goToSleepNoUpdateLocked(long eventTime, int reason) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason);
+ }
+
+ if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
+ || !mBootCompleted || !mSystemReady) {
+ return false;
+ }
+
+ switch (reason) {
+ case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
+ Slog.i(TAG, "Going to sleep due to device administration policy...");
+ break;
+ case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+ Slog.i(TAG, "Going to sleep due to screen timeout...");
+ break;
+ default:
+ Slog.i(TAG, "Going to sleep by user request...");
+ reason = PowerManager.GO_TO_SLEEP_REASON_USER;
+ break;
+ }
+
+ mLastSleepTime = eventTime;
+ mDirty |= DIRTY_WAKEFULNESS;
+ mWakefulness = WAKEFULNESS_ASLEEP;
+ mNotifier.onGoToSleepStarted(reason);
+ mSendGoToSleepFinishedNotificationWhenReady = true;
+ mSendWakeUpFinishedNotificationWhenReady = false;
+
+ // Report the number of wake locks that will be cleared by going to sleep.
+ int numWakeLocksCleared = 0;
+ final int numWakeLocks = mWakeLocks.size();
+ for (int i = 0; i < numWakeLocks; i++) {
+ final WakeLock wakeLock = mWakeLocks.get(i);
+ switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.FULL_WAKE_LOCK:
+ case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ case PowerManager.SCREEN_DIM_WAKE_LOCK:
+ numWakeLocksCleared += 1;
+ break;
+ }
+ }
+ EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);
+ return true;
+ }
+
+ /**
+ * Updates the global power state based on dirty bits recorded in mDirty.
+ *
+ * This is the main function that performs power state transitions.
+ * We centralize them here so that we can recompute the power state completely
+ * each time something important changes, and ensure that we do it the same
+ * way each time. The point is to gather all of the transition logic here.
+ */
+ private void updatePowerStateLocked() {
+ if (!mSystemReady || mDirty == 0) {
+ return;
+ }
+
+ // Phase 0: Basic state updates.
+ updateIsPoweredLocked(mDirty);
+ updateStayOnLocked(mDirty);
+
+ // Phase 1: Update wakefulness.
+ // Loop because the wake lock and user activity computations are influenced
+ // by changes in wakefulness.
+ final long now = SystemClock.uptimeMillis();
+ int dirtyPhase2 = 0;
+ for (;;) {
+ int dirtyPhase1 = mDirty;
+ dirtyPhase2 |= dirtyPhase1;
+ mDirty = 0;
+
+ updateWakeLockSummaryLocked(dirtyPhase1);
+ updateUserActivitySummaryLocked(now, dirtyPhase1);
+ if (!updateWakefulnessLocked(dirtyPhase1)) {
+ break;
+ }
+ }
+
+ // Phase 2: Update dreams and display power state.
+ updateDreamLocked(dirtyPhase2);
+ updateDisplayPowerStateLocked(dirtyPhase2);
+
+ // Phase 3: Send notifications, if needed.
+ sendPendingNotificationsLocked();
+
+ // Phase 4: Update suspend blocker.
+ // Because we might release the last suspend blocker here, we need to make sure
+ // we finished everything else first!
+ updateSuspendBlockerLocked();
+ }
+
+ private void sendPendingNotificationsLocked() {
+ if (mDisplayReady) {
+ if (mSendWakeUpFinishedNotificationWhenReady) {
+ mSendWakeUpFinishedNotificationWhenReady = false;
+ mNotifier.onWakeUpFinished();
+ }
+ if (mSendGoToSleepFinishedNotificationWhenReady) {
+ mSendGoToSleepFinishedNotificationWhenReady = false;
+ mNotifier.onGoToSleepFinished();
+ }
+ }
+ }
+
+ /**
+ * Updates the value of mIsPowered.
+ * Sets DIRTY_IS_POWERED if a change occurred.
+ */
+ private void updateIsPoweredLocked(int dirty) {
+ if ((dirty & DIRTY_BATTERY_STATE) != 0) {
+ boolean wasPowered = mIsPowered;
+ mIsPowered = mBatteryService.isPowered();
+
+ if (wasPowered != mIsPowered) {
+ mDirty |= DIRTY_IS_POWERED;
+
+ // Treat plugging and unplugging the devices as a user activity.
+ // Users find it disconcerting when they plug or unplug the device
+ // and it shuts off right away.
+ // Some devices also wake the device when plugged or unplugged because
+ // they don't have a charging LED.
+ final long now = SystemClock.uptimeMillis();
+ if (mWakeUpWhenPluggedOrUnpluggedConfig) {
+ wakeUpNoUpdateLocked(now);
+ }
+ userActivityNoUpdateLocked(
+ now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+ }
+ }
+ }
+
+ /**
+ * Updates the value of mStayOn.
+ * Sets DIRTY_STAY_ON if a change occurred.
+ */
+ private void updateStayOnLocked(int dirty) {
+ if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
+ if (mStayOnWhilePluggedInSetting != 0
+ && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
+ mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
+ } else {
+ mStayOn = false;
+ }
+ }
+ }
+
+ /**
+ * Updates the value of mWakeLockSummary to summarize the state of all active wake locks.
+ * Note that most wake-locks are ignored when the system is asleep.
+ *
+ * This function must have no other side-effects.
+ */
+ private void updateWakeLockSummaryLocked(int dirty) {
+ if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
+ mWakeLockSummary = 0;
+
+ final int numWakeLocks = mWakeLocks.size();
+ for (int i = 0; i < numWakeLocks; i++) {
+ final WakeLock wakeLock = mWakeLocks.get(i);
+ switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ mWakeLockSummary |= WAKE_LOCK_CPU;
+ break;
+ case PowerManager.FULL_WAKE_LOCK:
+ if (mWakefulness != WAKEFULNESS_ASLEEP) {
+ mWakeLockSummary |= WAKE_LOCK_CPU
+ | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
+ }
+ break;
+ case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ if (mWakefulness != WAKEFULNESS_ASLEEP) {
+ mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT;
+ }
+ break;
+ case PowerManager.SCREEN_DIM_WAKE_LOCK:
+ if (mWakefulness != WAKEFULNESS_ASLEEP) {
+ mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM;
+ }
+ break;
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ if (mWakefulness != WAKEFULNESS_ASLEEP) {
+ mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_PROXIMITY_SCREEN_OFF;
+ }
+ break;
+ }
+ }
+
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
+ + wakefulnessToString(mWakefulness)
+ + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
+ }
+ }
+ }
+
+ /**
+ * Updates the value of mUserActivitySummary to summarize the user requested
+ * state of the system such as whether the screen should be bright or dim.
+ * Note that user activity is ignored when the system is asleep.
+ *
+ * This function must have no other side-effects.
+ */
+ private void updateUserActivitySummaryLocked(long now, int dirty) {
+ // Update the status of the user activity timeout timer.
+ if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
+ mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
+
+ long nextTimeout = 0;
+ if (mWakefulness != WAKEFULNESS_ASLEEP) {
+ final int screenOffTimeout = getScreenOffTimeoutLocked();
+ final int screenDimDuration = getScreenDimDurationLocked();
+
+ mUserActivitySummary = 0;
+ if (mLastUserActivityTime >= mLastWakeTime) {
+ nextTimeout = mLastUserActivityTime
+ + screenOffTimeout - screenDimDuration;
+ if (now < nextTimeout) {
+ mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
+ } else {
+ nextTimeout = mLastUserActivityTime + screenOffTimeout;
+ if (now < nextTimeout) {
+ mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM;
+ }
+ }
+ }
+ if (mUserActivitySummary == 0
+ && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
+ nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
+ if (now < nextTimeout
+ && mDisplayPowerRequest.screenState
+ != DisplayPowerRequest.SCREEN_STATE_OFF) {
+ mUserActivitySummary = mDisplayPowerRequest.screenState
+ == DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
+ USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
+ }
+ }
+ if (mUserActivitySummary != 0) {
+ Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, nextTimeout);
+ }
+ } else {
+ mUserActivitySummary = 0;
+ }
+
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
+ + wakefulnessToString(mWakefulness)
+ + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ + ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
+ }
+ }
+ }
+
+ /**
+ * Called when a user activity timeout has occurred.
+ * Simply indicates that something about user activity has changed so that the new
+ * state can be recomputed when the power state is updated.
+ *
+ * This function must have no other side-effects besides setting the dirty
+ * bit and calling update power state. Wakefulness transitions are handled elsewhere.
+ */
+ private void handleUserActivityTimeout() { // runs on handler thread
+ synchronized (mLock) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "handleUserActivityTimeout");
+ }
+
+ mDirty |= DIRTY_USER_ACTIVITY;
+ updatePowerStateLocked();
+ }
+ }
+
+ private int getScreenOffTimeoutLocked() {
+ int timeout = mScreenOffTimeoutSetting;
+ if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
+ timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
+ }
+ return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT);
+ }
+
+ private int getScreenDimDurationLocked() {
+ return SCREEN_DIM_DURATION;
+ }
+
+ /**
+ * Updates the wakefulness of the device.
+ *
+ * This is the function that decides whether the device should start napping
+ * based on the current wake locks and user activity state. It may modify mDirty
+ * if the wakefulness changes.
+ *
+ * Returns true if the wakefulness changed and we need to restart power state calculation.
+ */
+ private boolean updateWakefulnessLocked(int dirty) {
+ boolean changed = false;
+ if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
+ | DIRTY_WAKEFULNESS | DIRTY_STAY_ON)) != 0) {
+ if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakefulnessLocked: Nap time...");
+ }
+ mWakefulness = WAKEFULNESS_NAPPING;
+ mDirty |= DIRTY_WAKEFULNESS;
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ // Also used when exiting a dream to determine whether we should go back
+ // to being fully awake or else go to sleep for good.
+ private boolean isItBedTimeYetLocked() {
+ return mBootCompleted && !mStayOn
+ && (mWakeLockSummary
+ & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) == 0
+ && (mUserActivitySummary
+ & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0;
+ }
+
+ /**
+ * Determines whether to post a message to the sandman to update the dream state.
+ */
+ private void updateDreamLocked(int dirty) {
+ if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_SETTINGS
+ | DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_BATTERY_STATE)) != 0) {
+ scheduleSandmanLocked();
+ }
+ }
+
+ private void scheduleSandmanLocked() {
+ if (!mSandmanScheduled) {
+ mSandmanScheduled = true;
+ Message msg = mHandler.obtainMessage(MSG_SANDMAN);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Called when the device enters or exits a napping or dreaming state.
+ *
+ * We do this asynchronously because we must call out of the power manager to start
+ * the dream and we don't want to hold our lock while doing so. There is a risk that
+ * the device will wake or go to sleep in the meantime so we have to handle that case.
+ */
+ private void handleSandman() { // runs on handler thread
+ // Handle preconditions.
+ boolean startDreaming = false;
+ synchronized (mLock) {
+ mSandmanScheduled = false;
+ boolean canDream = canDreamLocked();
+ if (DEBUG_SPEW) {
+ Log.d(TAG, "handleSandman: canDream=" + canDream
+ + ", mWakefulness=" + wakefulnessToString(mWakefulness));
+ }
+
+ if (canDream && mWakefulness == WAKEFULNESS_NAPPING) {
+ startDreaming = true;
+ }
+ }
+
+ // Get the dream manager, if needed.
+ if (startDreaming && mDreamManager == null) {
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService("dreams"));
+ if (mDreamManager == null) {
+ Slog.w(TAG, "Unable to find IDreamManager.");
+ }
+ }
+
+ // Start dreaming if needed.
+ // We only control the dream on the handler thread, so we don't need to worry about
+ // concurrent attempts to start or stop the dream.
+ boolean isDreaming = false;
+ if (mDreamManager != null) {
+ try {
+ isDreaming = mDreamManager.isDreaming();
+ if (startDreaming && !isDreaming) {
+ Slog.i(TAG, "Entering dreamland.");
+ mDreamManager.dream();
+ isDreaming = mDreamManager.isDreaming();
+ if (!isDreaming) {
+ Slog.i(TAG, "Could not enter dreamland. Sleep will be dreamless.");
+ }
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ // Update dream state.
+ // We might need to stop the dream again if the preconditions changed.
+ boolean continueDreaming = false;
+ synchronized (mLock) {
+ if (isDreaming && canDreamLocked()) {
+ if (mWakefulness == WAKEFULNESS_NAPPING) {
+ mWakefulness = WAKEFULNESS_DREAMING;
+ mDirty |= DIRTY_WAKEFULNESS;
+ updatePowerStateLocked();
+ continueDreaming = true;
+ } else if (mWakefulness == WAKEFULNESS_DREAMING) {
+ continueDreaming = true;
+ }
+ }
+ if (!continueDreaming) {
+ handleDreamFinishedLocked();
+ }
+
+ // Allow the sandman to detect when the dream has ended.
+ // FIXME: The DreamManagerService should tell us explicitly.
+ if (mWakefulness == WAKEFULNESS_DREAMING
+ || mWakefulness == WAKEFULNESS_NAPPING) {
+ if (!mSandmanScheduled) {
+ mSandmanScheduled = true;
+ Message msg = mHandler.obtainMessage(MSG_SANDMAN);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, 1000);
+ }
+ }
+ }
+
+ // Stop dreaming if needed.
+ // It's possible that something else changed to make us need to start the dream again.
+ // If so, then the power manager will have posted another message to the handler
+ // to take care of it later.
+ if (mDreamManager != null) {
+ try {
+ if (!continueDreaming && isDreaming) {
+ Slog.i(TAG, "Leaving dreamland.");
+ mDreamManager.awaken();
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ /**
+ * Returns true if the device is allowed to dream in its current state,
+ * assuming there has been no recent user activity and no wake locks are held.
+ */
+ private boolean canDreamLocked() {
+ return mIsPowered
+ && mDreamsSupportedConfig
+ && mDreamsEnabledSetting
+ && mDreamsActivateOnSleepSetting
+ && !mBatteryService.isBatteryLow();
+ }
+
+ /**
+ * Called when a dream is ending to figure out what to do next.
+ */
+ private void handleDreamFinishedLocked() {
+ if (mWakefulness == WAKEFULNESS_NAPPING
+ || mWakefulness == WAKEFULNESS_DREAMING) {
+ if (isItBedTimeYetLocked()) {
+ goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+ updatePowerStateLocked();
+ } else {
+ wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
+ updatePowerStateLocked();
+ }
+ }
+ }
+
+
+ /**
+ * Updates the display power state asynchronously.
+ * When the update is finished, mDisplayReady will be set to true. The display
+ * controller posts a message to tell us when the actual display power state
+ * has been updated so we come back here to double-check and finish up.
+ *
+ * This function recalculates the display power state each time.
+ */
+ private void updateDisplayPowerStateLocked(int dirty) {
+ if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
+ | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
+ | DIRTY_SETTINGS)) != 0) {
+ int newScreenState = getDesiredScreenPowerState();
+ if (newScreenState != mDisplayPowerRequest.screenState) {
+ if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF
+ && mDisplayPowerRequest.screenState
+ != DisplayPowerRequest.SCREEN_STATE_OFF) {
+ mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime();
+ }
+
+ mDisplayPowerRequest.screenState = newScreenState;
+ nativeSetPowerState(
+ newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF,
+ newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT);
+ }
+
+ int screenBrightness = mScreenBrightnessSettingDefault;
+ float screenAutoBrightnessAdjustment = 0.0f;
+ boolean autoBrightness = (mScreenBrightnessModeSetting ==
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
+ screenBrightness = mScreenBrightnessOverrideFromWindowManager;
+ autoBrightness = false;
+ } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
+ screenBrightness = mTemporaryScreenBrightnessSettingOverride;
+ } else if (isValidBrightness(mScreenBrightnessSetting)) {
+ screenBrightness = mScreenBrightnessSetting;
+ }
+ if (autoBrightness) {
+ screenBrightness = mScreenBrightnessSettingDefault;
+ if (isValidAutoBrightnessAdjustment(
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
+ screenAutoBrightnessAdjustment =
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
+ } else if (isValidAutoBrightnessAdjustment(
+ mScreenAutoBrightnessAdjustmentSetting)) {
+ screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
+ }
+ }
+ screenBrightness = Math.max(Math.min(screenBrightness,
+ mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
+ screenAutoBrightnessAdjustment = Math.max(Math.min(
+ screenAutoBrightnessAdjustment, 1.0f), -1.0f);
+ mDisplayPowerRequest.screenBrightness = screenBrightness;
+ mDisplayPowerRequest.screenAutoBrightnessAdjustment =
+ screenAutoBrightnessAdjustment;
+ mDisplayPowerRequest.useAutoBrightness = autoBrightness;
+
+ mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
+
+ mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
+ mRequestWaitForNegativeProximity);
+ mRequestWaitForNegativeProximity = false;
+
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateScreenStateLocked: displayReady=" + mDisplayReady
+ + ", newScreenState=" + newScreenState
+ + ", mWakefulness=" + mWakefulness
+ + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+ + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ + ", mBootCompleted=" + mBootCompleted);
+ }
+ }
+ }
+
+ private static boolean isValidBrightness(int value) {
+ return value >= 0 && value <= 255;
+ }
+
+ private static boolean isValidAutoBrightnessAdjustment(float value) {
+ // Handles NaN by always returning false.
+ return value >= -1.0f && value <= 1.0f;
+ }
+
+ private int getDesiredScreenPowerState() {
+ if (mWakefulness == WAKEFULNESS_ASLEEP) {
+ return DisplayPowerRequest.SCREEN_STATE_OFF;
+ }
+
+ if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
+ || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+ || !mBootCompleted) {
+ return DisplayPowerRequest.SCREEN_STATE_BRIGHT;
+ }
+
+ return DisplayPowerRequest.SCREEN_STATE_DIM;
+ }
+
+ private final DisplayPowerController.Callbacks mDisplayPowerControllerCallbacks =
+ new DisplayPowerController.Callbacks() {
+ @Override
+ public void onStateChanged() {
+ mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
+ updatePowerStateLocked();
+ }
+
+ @Override
+ public void onProximityNegative() {
+ userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+ updatePowerStateLocked();
+ }
+ };
+
+ private boolean shouldUseProximitySensorLocked() {
+ return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
+ }
+
+ /**
+ * Updates the suspend blocker that keeps the CPU alive.
+ *
+ * This function must have no other side-effects.
+ */
+ private void updateSuspendBlockerLocked() {
+ boolean wantCpu = isCpuNeededLocked();
+ if (wantCpu != mHoldingWakeLockSuspendBlocker) {
+ mHoldingWakeLockSuspendBlocker = wantCpu;
+ if (wantCpu) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateSuspendBlockerLocked: Acquiring suspend blocker.");
+ }
+ mWakeLockSuspendBlocker.acquire();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "updateSuspendBlockerLocked: Releasing suspend blocker.");
+ }
+ mWakeLockSuspendBlocker.release();
+ }
+ }
+ }
+
+ private boolean isCpuNeededLocked() {
+ return !mBootCompleted
+ || mWakeLockSummary != 0
+ || mUserActivitySummary != 0
+ || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF
+ || !mDisplayReady;
+ }
+
+ @Override // Binder call
+ public boolean isScreenOn() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isScreenOnInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean isScreenOnInternal() {
+ synchronized (mLock) {
+ return !mSystemReady
+ || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF;
+ }
+ }
+
+ private void handleBatteryStateChangedLocked() {
+ mDirty |= DIRTY_BATTERY_STATE;
+ updatePowerStateLocked();
+ }
+
+ private void handleBootCompletedLocked() {
+ final long now = SystemClock.uptimeMillis();
+ mBootCompleted = true;
+ mDirty |= DIRTY_BOOT_COMPLETED;
+ userActivityNoUpdateLocked(
+ now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+ updatePowerStateLocked();
+ }
+
+ private void handleDockStateChangedLocked(int dockState) {
+ // TODO
+ }
+
+ /**
+ * Reboot the device immediately, passing 'reason' (may be null)
+ * to the underlying __reboot system call. Should not return.
+ */
+ @Override // Binder call
+ public void reboot(String reason) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ rebootInternal(reason);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void rebootInternal(final String reason) {
+ if (mHandler == null || !mSystemReady) {
+ throw new IllegalStateException("Too early to call reboot()");
+ }
+
+ Runnable runnable = new Runnable() {
+ public void run() {
+ synchronized (this) {
+ ShutdownThread.reboot(mContext, reason, false);
+ }
+ }
+ };
+
+ // ShutdownThread must run on a looper capable of displaying the UI.
+ Message msg = Message.obtain(mHandler, runnable);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+
+ // PowerManager.reboot() is documented not to return so just wait for the inevitable.
+ synchronized (runnable) {
+ while (true) {
+ try {
+ runnable.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Crash the runtime (causing a complete restart of the Android framework).
+ * Requires REBOOT permission. Mostly for testing. Should not return.
+ */
+ @Override // Binder call
+ public void crash(String message) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ crashInternal(message);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void crashInternal(final String message) {
+ Thread t = new Thread("PowerManagerService.crash()") {
+ public void run() {
+ throw new RuntimeException(message);
+ }
+ };
+ try {
+ t.start();
+ t.join();
+ } catch (InterruptedException e) {
+ Log.wtf(TAG, e);
+ }
+ }
+
+ @Override // Binder call
+ public void clearUserActivityTimeout(long now, long timeout) {
+ // TODO Auto-generated method stub
+ // Only used by phone app, delete this
+ }
+
+ @Override // Binder call
+ public void setPokeLock(int pokey, IBinder lock, String tag) {
+ // TODO Auto-generated method stub
+ // Only used by phone app, delete this
+ }
+
+ /**
+ * Set the setting that determines whether the device stays on when plugged in.
+ * The argument is a bit string, with each bit specifying a power source that,
+ * when the device is connected to that source, causes the device to stay on.
+ * See {@link android.os.BatteryManager} for the list of power sources that
+ * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC}
+ * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB}
+ *
+ * Used by "adb shell svc power stayon ..."
+ *
+ * @param val an {@code int} containing the bits that specify which power sources
+ * should cause the device to stay on.
+ */
+ @Override // Binder call
+ public void setStayOnSetting(int val) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setStayOnSettingInternal(val);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setStayOnSettingInternal(int val) {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.STAY_ON_WHILE_PLUGGED_IN, val);
+ }
+
+ /**
+ * Used by device administration to set the maximum screen off timeout.
+ *
+ * This method must only be called by the device administration policy manager.
+ */
+ @Override // Binder call
+ public void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setMaximumScreenOffTimeoutFromDeviceAdminInternal(timeMs);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) {
+ synchronized (mLock) {
+ mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs;
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
+
+ private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() {
+ return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0
+ && mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE;
+ }
+
+ @Override // Binder call
+ public void preventScreenOn(boolean prevent) {
+ // TODO Auto-generated method stub
+ // Only used by phone app, delete this
+ }
+
+ /**
+ * Used by the phone application to make the attention LED flash when ringing.
+ */
+ @Override // Binder call
+ public void setAttentionLight(boolean on, int color) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setAttentionLightInternal(on, color);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setAttentionLightInternal(boolean on, int color) {
+ LightsService.Light light;
+ synchronized (mLock) {
+ if (!mSystemReady) {
+ return;
+ }
+ light = mAttentionLight;
+ }
+
+ // Control light outside of lock.
+ light.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
+ }
+
+ /**
+ * Used by the Watchdog.
+ */
+ public long timeSinceScreenWasLastOn() {
+ synchronized (mLock) {
+ if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+ return 0;
+ }
+ return SystemClock.elapsedRealtime() - mLastScreenOffEventElapsedRealTime;
+ }
+ }
+
+ /**
+ * Used by the window manager to override the screen brightness based on the
+ * current foreground activity.
+ *
+ * This method must only be called by the window manager.
+ *
+ * @param brightness The overridden brightness, or -1 to disable the override.
+ */
+ public void setScreenBrightnessOverrideFromWindowManager(int brightness) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setScreenBrightnessOverrideFromWindowManagerInternal(brightness);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) {
+ synchronized (mLock) {
+ if (mScreenBrightnessOverrideFromWindowManager != brightness) {
+ mScreenBrightnessOverrideFromWindowManager = brightness;
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
+ }
+
+ /**
+ * Used by the window manager to override the button brightness based on the
+ * current foreground activity.
+ *
+ * This method must only be called by the window manager.
+ *
+ * @param brightness The overridden brightness, or -1 to disable the override.
+ */
+ public void setButtonBrightnessOverrideFromWindowManager(int brightness) {
+ // Do nothing.
+ // Button lights are not currently supported in the new implementation.
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ }
+
+ /**
+ * Used by the settings application and brightness control widgets to
+ * temporarily override the current screen brightness setting so that the
+ * user can observe the effect of an intended settings change without applying
+ * it immediately.
+ *
+ * The override will be canceled when the setting value is next updated.
+ *
+ * @param brightness The overridden brightness.
+ *
+ * @see Settings.System#SCREEN_BRIGHTNESS
+ */
+ @Override // Binder call
+ public void setTemporaryScreenBrightnessSettingOverride(int brightness) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setTemporaryScreenBrightnessSettingOverrideInternal(brightness);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) {
+ synchronized (mLock) {
+ if (mTemporaryScreenBrightnessSettingOverride != brightness) {
+ mTemporaryScreenBrightnessSettingOverride = brightness;
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
+ }
+
+ /**
+ * Used by the settings application and brightness control widgets to
+ * temporarily override the current screen auto-brightness adjustment setting so that the
+ * user can observe the effect of an intended settings change without applying
+ * it immediately.
+ *
+ * The override will be canceled when the setting value is next updated.
+ *
+ * @param adj The overridden brightness, or Float.NaN to disable the override.
+ *
+ * @see Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ
+ */
+ @Override // Binder call
+ public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) {
+ synchronized (mLock) {
+ // Note: This condition handles NaN because NaN is not equal to any other
+ // value, including itself.
+ if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) {
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj;
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
+ }
+
+ /**
+ * Low-level function turn the device off immediately, without trying
+ * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
+ */
+ public static void lowLevelShutdown() {
+ nativeShutdown();
+ }
+
+ /**
+ * Low-level function to reboot the device.
+ *
+ * @param reason code to pass to the kernel (e.g. "recovery"), or null.
+ * @throws IOException if reboot fails for some reason (eg, lack of
+ * permission)
+ */
+ public static void lowLevelReboot(String reason) throws IOException {
+ nativeReboot(reason);
+ }
+
+ @Override // Watchdog.Monitor implementation
+ public void monitor() {
+ // Grab and release lock for watchdog monitor to detect deadlocks.
+ synchronized (mLock) {
+ }
+ }
+
+ @Override // Binder call
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump PowerManager from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ pw.println("POWER MANAGER (dumpsys power)\n");
+
+ final DisplayPowerController dpc;
+ synchronized (mLock) {
+ pw.println("Power Manager State:");
+ pw.println(" mDirty=0x" + Integer.toHexString(mDirty));
+ pw.println(" mWakefulness=" + wakefulnessToString(mWakefulness));
+ pw.println(" mIsPowered=" + mIsPowered);
+ pw.println(" mStayOn=" + mStayOn);
+ pw.println(" mBootCompleted=" + mBootCompleted);
+ pw.println(" mSystemReady=" + mSystemReady);
+ pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
+ pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
+ pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
+ pw.println(" mSandmanScheduled=" + mSandmanScheduled);
+ pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
+ pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
+ pw.println(" mSendWakeUpFinishedNotificationWhenReady="
+ + mSendWakeUpFinishedNotificationWhenReady);
+ pw.println(" mSendGoToSleepFinishedNotificationWhenReady="
+ + mSendGoToSleepFinishedNotificationWhenReady);
+ pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
+ pw.println(" mLastUserActivityTimeNoChangeLights="
+ + TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights));
+ pw.println(" mDisplayReady=" + mDisplayReady);
+ pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker);
+
+ pw.println();
+ pw.println("Settings and Configuration:");
+ pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig);
+ pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting);
+ pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
+ pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting);
+ pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin="
+ + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
+ + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
+ pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
+ pw.println(" mScreenBrightnessSetting=" + mScreenBrightnessSetting);
+ pw.println(" mScreenAutoBrightnessAdjustmentSetting="
+ + mScreenAutoBrightnessAdjustmentSetting);
+ pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting);
+ pw.println(" mScreenBrightnessOverrideFromWindowManager="
+ + mScreenBrightnessOverrideFromWindowManager);
+ pw.println(" mTemporaryScreenBrightnessSettingOverride="
+ + mTemporaryScreenBrightnessSettingOverride);
+ pw.println(" mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
+ + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride);
+ pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
+ pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
+ pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
+
+ pw.println();
+ pw.println("Wake Locks: size=" + mWakeLocks.size());
+ for (WakeLock wl : mWakeLocks) {
+ pw.println(" " + wl);
+ }
+
+ pw.println();
+ pw.println("Suspend Blockers: size=" + mSuspendBlockers.size());
+ for (SuspendBlocker sb : mSuspendBlockers) {
+ pw.println(" " + sb);
+ }
+
+ dpc = mDisplayPowerController;
+ }
+
+ if (dpc != null) {
+ dpc.dump(pw);
+ }
+ }
+
+ private SuspendBlocker createSuspendBlockerLocked(String name) {
+ SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
+ mSuspendBlockers.add(suspendBlocker);
+ return suspendBlocker;
+ }
+
+ private static String wakefulnessToString(int wakefulness) {
+ switch (wakefulness) {
+ case WAKEFULNESS_ASLEEP:
+ return "Asleep";
+ case WAKEFULNESS_AWAKE:
+ return "Awake";
+ case WAKEFULNESS_DREAMING:
+ return "Dreaming";
+ case WAKEFULNESS_NAPPING:
+ return "Napping";
+ default:
+ return Integer.toString(wakefulness);
+ }
+ }
+
+ private static WorkSource copyWorkSource(WorkSource workSource) {
+ return workSource != null ? new WorkSource(workSource) : null;
+ }
+
+ private final class BatteryReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ handleBatteryStateChangedLocked();
+ }
+ }
+ }
+
+ private final class BootCompletedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ handleBootCompletedLocked();
+ }
+ }
+ }
+
+ private final class DockReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ handleDockStateChangedLocked(dockState);
+ }
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ synchronized (mLock) {
+ handleSettingsChangedLocked();
+ }
+ }
+ }
+
+ private final WindowManagerPolicy.ScreenOnListener mScreenOnListener =
+ new WindowManagerPolicy.ScreenOnListener() {
+ @Override
+ public void onScreenOn() {
+ }
+ };
+
+ /**
+ * Handler for asynchronous operations performed by the power manager.
+ */
+ private final class PowerManagerHandler extends Handler {
+ public PowerManagerHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_USER_ACTIVITY_TIMEOUT:
+ handleUserActivityTimeout();
+ break;
+ case MSG_SANDMAN:
+ handleSandman();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Represents a wake lock that has been acquired by an application.
+ */
+ private final class WakeLock implements IBinder.DeathRecipient {
+ public final IBinder mLock;
+ public int mFlags;
+ public String mTag;
+ public WorkSource mWorkSource;
+ public int mOwnerUid;
+ public int mOwnerPid;
+
+ public WakeLock(IBinder lock, int flags, String tag, WorkSource workSource,
+ int ownerUid, int ownerPid) {
+ mLock = lock;
+ mFlags = flags;
+ mTag = tag;
+ mWorkSource = copyWorkSource(workSource);
+ mOwnerUid = ownerUid;
+ mOwnerPid = ownerPid;
+ }
+
+ @Override
+ public void binderDied() {
+ PowerManagerService.this.handleWakeLockDeath(this);
+ }
+
+ public boolean hasSameProperties(int flags, String tag, WorkSource workSource,
+ int ownerUid, int ownerPid) {
+ return mFlags == flags
+ && mTag.equals(tag)
+ && hasSameWorkSource(workSource)
+ && mOwnerUid == ownerUid
+ && mOwnerPid == ownerPid;
+ }
+
+ public void updateProperties(int flags, String tag, WorkSource workSource,
+ int ownerUid, int ownerPid) {
+ mFlags = flags;
+ mTag = tag;
+ updateWorkSource(workSource);
+ mOwnerUid = ownerUid;
+ mOwnerPid = ownerPid;
+ }
+
+ public boolean hasSameWorkSource(WorkSource workSource) {
+ return Objects.equal(mWorkSource, workSource);
+ }
+
+ public void updateWorkSource(WorkSource workSource) {
+ mWorkSource = copyWorkSource(workSource);
+ }
+
+ @Override
+ public String toString() {
+ return getLockLevelString()
+ + " '" + mTag + "'" + getLockFlagsString()
+ + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")";
+ }
+
+ private String getLockLevelString() {
+ switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.FULL_WAKE_LOCK:
+ return "FULL_WAKE_LOCK ";
+ case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ return "SCREEN_BRIGHT_WAKE_LOCK ";
+ case PowerManager.SCREEN_DIM_WAKE_LOCK:
+ return "SCREEN_DIM_WAKE_LOCK ";
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ return "PARTIAL_WAKE_LOCK ";
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
+ default:
+ return "??? ";
+ }
+ }
+
+ private String getLockFlagsString() {
+ String result = "";
+ if ((mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
+ result += " ACQUIRE_CAUSES_WAKEUP";
+ }
+ if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
+ result += " ON_AFTER_RELEASE";
+ }
+ return result;
+ }
+ }
+
+ private final class SuspendBlockerImpl implements SuspendBlocker {
+ private final String mName;
+ private int mReferenceCount;
+
+ public SuspendBlockerImpl(String name) {
+ mName = name;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mReferenceCount != 0) {
+ Log.wtf(TAG, "Suspend blocker \"" + mName
+ + "\" was finalized without being released!");
+ mReferenceCount = 0;
+ nativeReleaseSuspendBlocker(mName);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ public void acquire() {
+ synchronized (this) {
+ mReferenceCount += 1;
+ if (mReferenceCount == 1) {
+ nativeAcquireSuspendBlocker(mName);
+ }
+ }
+ }
+
+ @Override
+ public void release() {
+ synchronized (this) {
+ mReferenceCount -= 1;
+ if (mReferenceCount == 0) {
+ nativeReleaseSuspendBlocker(mName);
+ } else if (mReferenceCount < 0) {
+ Log.wtf(TAG, "Suspend blocker \"" + mName
+ + "\" was released without being acquired!", new Throwable());
+ mReferenceCount = 0;
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ return mName + ": ref count=" + mReferenceCount;
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/power/RampAnimator.java b/services/java/com/android/server/power/RampAnimator.java
new file mode 100644
index 0000000..6f063c3
--- /dev/null
+++ b/services/java/com/android/server/power/RampAnimator.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.animation.ValueAnimator;
+import android.util.IntProperty;
+import android.view.Choreographer;
+
+/**
+ * A custom animator that progressively updates a property value at
+ * a given variable rate until it reaches a particular target value.
+ */
+final class RampAnimator<T> {
+ private final T mObject;
+ private final IntProperty<T> mProperty;
+ private final Choreographer mChoreographer;
+
+ private int mCurrentValue;
+ private int mTargetValue;
+ private int mRate;
+
+ private boolean mAnimating;
+ private float mAnimatedValue; // higher precision copy of mCurrentValue
+ private long mLastFrameTimeNanos;
+
+ private boolean mFirstTime = true;
+
+ public RampAnimator(T object, IntProperty<T> property) {
+ mObject = object;
+ mProperty = property;
+ mChoreographer = Choreographer.getInstance();
+ }
+
+ /**
+ * Starts animating towards the specified value.
+ *
+ * If this is the first time the property is being set, the value jumps
+ * directly to the target.
+ *
+ * @param target The target value.
+ * @param rate The convergence rate, in units per second.
+ * @return True if the target differs from the previous target.
+ */
+ public boolean animateTo(int target, int rate) {
+ // Immediately jump to the target the first time.
+ if (mFirstTime) {
+ mFirstTime = false;
+ mProperty.setValue(mObject, target);
+ mCurrentValue = target;
+ return true;
+ }
+
+ // Adjust the rate based on the closest target.
+ // If a faster rate is specified, then use the new rate so that we converge
+ // more rapidly based on the new request.
+ // If a slower rate is specified, then use the new rate only if the current
+ // value is somewhere in between the new and the old target meaning that
+ // we will be ramping in a different direction to get there.
+ // Otherwise, continue at the previous rate.
+ if (!mAnimating
+ || rate > mRate
+ || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
+ || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
+ mRate = rate;
+ }
+
+ final boolean changed = (mTargetValue != target);
+ mTargetValue = target;
+
+ // Start animating.
+ if (!mAnimating && target != mCurrentValue) {
+ mAnimating = true;
+ mAnimatedValue = mCurrentValue;
+ mLastFrameTimeNanos = System.nanoTime();
+ postCallback();
+ }
+
+ return changed;
+ }
+
+ private void postCallback() {
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mCallback, null);
+ }
+
+ private final Runnable mCallback = new Runnable() {
+ @Override // Choreographer callback
+ public void run() {
+ final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+ final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
+ * 0.000000001f;
+ final float amount = timeDelta * mRate / ValueAnimator.getDurationScale();
+ mLastFrameTimeNanos = frameTimeNanos;
+
+ // Advance the animated value towards the target at the specified rate
+ // and clamp to the target. This gives us the new current value but
+ // we keep the animated value around to allow for fractional increments
+ // towards the target.
+ int oldCurrentValue = mCurrentValue;
+ if (mTargetValue > mCurrentValue) {
+ mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
+ } else {
+ mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
+ }
+ mCurrentValue = (int)Math.round(mAnimatedValue);
+
+ if (oldCurrentValue != mCurrentValue) {
+ mProperty.setValue(mObject, mCurrentValue);
+ }
+
+ if (mTargetValue != mCurrentValue) {
+ postCallback();
+ } else {
+ mAnimating = false;
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/pm/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index 3675d41..a3770d7 100644
--- a/services/java/com/android/server/pm/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -15,7 +15,7 @@
*/
-package com.android.server.pm;
+package com.android.server.power;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
@@ -23,7 +23,7 @@ import android.app.Dialog;
import android.app.IActivityManager;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothManager;
import android.nfc.NfcAdapter;
import android.nfc.INfcAdapter;
import android.content.BroadcastReceiver;
@@ -43,7 +43,6 @@ import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
import com.android.internal.telephony.ITelephony;
-import com.android.server.PowerManagerService;
import android.util.Log;
import android.view.WindowManager;
@@ -385,9 +384,9 @@ public final class ShutdownThread extends Thread {
INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
final ITelephony phone =
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- final IBluetooth bluetooth =
- IBluetooth.Stub.asInterface(ServiceManager.checkService(
- BluetoothAdapter.BLUETOOTH_SERVICE));
+ final IBluetoothManager bluetooth =
+ IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
+ BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
try {
nfcOff = nfc == null ||
@@ -402,8 +401,7 @@ public final class ShutdownThread extends Thread {
}
try {
- bluetoothOff = bluetooth == null ||
- bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
+ bluetoothOff = bluetooth == null || !bluetooth.isEnabled();
if (!bluetoothOff) {
Log.w(TAG, "Disabling Bluetooth...");
bluetooth.disable(false); // disable but don't persist new state
@@ -429,8 +427,7 @@ public final class ShutdownThread extends Thread {
while (SystemClock.elapsedRealtime() < endTime) {
if (!bluetoothOff) {
try {
- bluetoothOff =
- bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
+ bluetoothOff = !bluetooth.isEnabled();
} catch (RemoteException ex) {
Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
bluetoothOff = true;
diff --git a/services/java/com/android/server/power/SuspendBlocker.java b/services/java/com/android/server/power/SuspendBlocker.java
new file mode 100644
index 0000000..70b278a
--- /dev/null
+++ b/services/java/com/android/server/power/SuspendBlocker.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+/**
+ * Low-level suspend blocker mechanism equivalent to holding a partial wake lock.
+ *
+ * This interface is used internally to avoid introducing internal dependencies
+ * on the high-level wake lock mechanism.
+ */
+interface SuspendBlocker {
+ /**
+ * Acquires the suspend blocker.
+ * Prevents the CPU from going to sleep.
+ *
+ * Calls to acquire() nest and must be matched by the same number
+ * of calls to release().
+ */
+ void acquire();
+
+ /**
+ * Releases the suspend blocker.
+ * Allows the CPU to go to sleep if no other suspend blockers are held.
+ *
+ * It is an error to call release() if the suspend blocker has not been acquired.
+ * The system may crash.
+ */
+ void release();
+}
diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java
new file mode 100644
index 0000000..a3b45c7
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDebuggingManager.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions an
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.Base64;
+
+import java.lang.Thread;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+public class UsbDebuggingManager implements Runnable {
+ private static final String TAG = "UsbDebuggingManager";
+ private static final boolean DEBUG = false;
+
+ private final String ADBD_SOCKET = "adbd";
+ private final String ADB_DIRECTORY = "misc/adb";
+ private final String ADB_KEYS_FILE = "adb_keys";
+ private final int BUFFER_SIZE = 4096;
+
+ private final Context mContext;
+ private final Thread mThread;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
+ private boolean mAdbEnabled = false;
+ private String mFingerprints;
+ private LocalSocket mSocket = null;
+ private OutputStream mOutputStream = null;
+
+ public UsbDebuggingManager(Context context) {
+ mThread = new Thread(this);
+ mHandlerThread = new HandlerThread("UsbDebuggingHandler");
+ mHandlerThread.start();
+ mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper());
+ mContext = context;
+ }
+
+ private void listenToSocket() throws IOException {
+ try {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
+ LocalSocketAddress.Namespace.RESERVED);
+ InputStream inputStream = null;
+
+ mSocket = new LocalSocket();
+ mSocket.connect(address);
+
+ mOutputStream = mSocket.getOutputStream();
+ inputStream = mSocket.getInputStream();
+
+ while (true) {
+ int count = inputStream.read(buffer);
+ if (count < 0) {
+ Slog.e(TAG, "got " + count + " reading");
+ break;
+ }
+
+ if (buffer[0] == 'P' && buffer[1] == 'K') {
+ String key = new String(Arrays.copyOfRange(buffer, 2, count));
+ Slog.d(TAG, "Received public key: " + key);
+ Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
+ msg.obj = key;
+ mHandler.sendMessage(msg);
+ }
+ else {
+ Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2))));
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ Slog.e(TAG, "Communication error: ", ex);
+ throw ex;
+ } finally {
+ closeSocket();
+ }
+ }
+
+ @Override
+ public void run() {
+ while (mAdbEnabled) {
+ try {
+ listenToSocket();
+ } catch (Exception e) {
+ /* Don't loop too fast if adbd dies, before init restarts it */
+ SystemClock.sleep(1000);
+ }
+ }
+ }
+
+ private void closeSocket() {
+ try {
+ mOutputStream.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed closing output stream: " + e);
+ }
+
+ try {
+ mSocket.close();
+ } catch (IOException ex) {
+ Slog.e(TAG, "Failed closing socket: " + ex);
+ }
+ }
+
+ private void sendResponse(String msg) {
+ if (mOutputStream != null) {
+ try {
+ mOutputStream.write(msg.getBytes());
+ }
+ catch (IOException ex) {
+ Slog.e(TAG, "Failed to write response:", ex);
+ }
+ }
+ }
+
+ class UsbDebuggingHandler extends Handler {
+ private static final int MESSAGE_ADB_ENABLED = 1;
+ private static final int MESSAGE_ADB_DISABLED = 2;
+ private static final int MESSAGE_ADB_ALLOW = 3;
+ private static final int MESSAGE_ADB_DENY = 4;
+ private static final int MESSAGE_ADB_CONFIRM = 5;
+
+ public UsbDebuggingHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_ADB_ENABLED:
+ if (mAdbEnabled)
+ break;
+
+ mAdbEnabled = true;
+
+ mThread.start();
+
+ break;
+
+ case MESSAGE_ADB_DISABLED:
+ if (!mAdbEnabled)
+ break;
+
+ mAdbEnabled = false;
+ closeSocket();
+
+ try {
+ mThread.join();
+ } catch (Exception ex) {
+ }
+
+ mOutputStream = null;
+ mSocket = null;
+
+ case MESSAGE_ADB_ALLOW: {
+ String key = (String)msg.obj;
+ String fingerprints = getFingerprints(key);
+
+ if (!fingerprints.equals(mFingerprints)) {
+ Slog.e(TAG, "Fingerprints do not match. Got "
+ + fingerprints + ", expected " + mFingerprints);
+ break;
+ }
+
+ if (msg.arg1 == 1) {
+ writeKey(key);
+ }
+
+ sendResponse("OK");
+ break;
+ }
+
+ case MESSAGE_ADB_DENY:
+ sendResponse("NO");
+ break;
+
+ case MESSAGE_ADB_CONFIRM: {
+ String key = (String)msg.obj;
+ mFingerprints = getFingerprints(key);
+ showConfirmationDialog(key, mFingerprints);
+ break;
+ }
+ }
+ }
+ }
+
+ private String getFingerprints(String key) {
+ String hex = "0123456789ABCDEF";
+ StringBuilder sb = new StringBuilder();
+ MessageDigest digester;
+
+ try {
+ digester = MessageDigest.getInstance("MD5");
+ } catch (Exception ex) {
+ Slog.e(TAG, "Error getting digester: " + ex);
+ return "";
+ }
+
+ byte[] base64_data = key.split("\\s+")[0].getBytes();
+ byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
+
+ for (int i = 0; i < digest.length; i++) {
+ sb.append(hex.charAt((digest[i] >> 4) & 0xf));
+ sb.append(hex.charAt(digest[i] & 0xf));
+ if (i < digest.length - 1)
+ sb.append(":");
+ }
+ return sb.toString();
+ }
+
+ private void showConfirmationDialog(String key, String fingerprints) {
+ Intent dialogIntent = new Intent();
+
+ dialogIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbDebuggingActivity");
+ dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ dialogIntent.putExtra("key", key);
+ dialogIntent.putExtra("fingerprints", fingerprints);
+ try {
+ mContext.startActivity(dialogIntent);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "unable to start UsbDebuggingActivity");
+ }
+ }
+
+ private void writeKey(String key) {
+ File dataDir = Environment.getDataDirectory();
+ File adbDir = new File(dataDir, ADB_DIRECTORY);
+
+ if (!adbDir.exists()) {
+ Slog.e(TAG, "ADB data directory does not exist");
+ return;
+ }
+
+ try {
+ File keyFile = new File(adbDir, ADB_KEYS_FILE);
+
+ if (!keyFile.exists()) {
+ keyFile.createNewFile();
+ FileUtils.setPermissions(keyFile.toString(),
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+ FileUtils.S_IRGRP, -1, -1);
+ }
+
+ FileOutputStream fo = new FileOutputStream(keyFile, true);
+ fo.write(key.getBytes());
+ fo.write('\n');
+ fo.close();
+ }
+ catch (IOException ex) {
+ Slog.e(TAG, "Error writing key:" + ex);
+ }
+ }
+
+
+ public void setAdbEnabled(boolean enabled) {
+ mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
+ : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
+ }
+
+ public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
+ Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
+ msg.arg1 = alwaysAllow ? 1 : 0;
+ msg.obj = publicKey;
+ mHandler.sendMessage(msg);
+ }
+
+ public void denyUsbDebugging() {
+ mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
+ }
+
+
+ public void dump(FileDescriptor fd, PrintWriter pw) {
+ pw.println(" USB Debugging State:");
+ pw.println(" Connected to adbd: " + (mOutputStream != null));
+ pw.println(" Last key received: " + mFingerprints);
+ pw.println(" User keys:");
+ try {
+ pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
+ } catch (IOException e) {
+ pw.println("IOException: " + e);
+ }
+ pw.println(" System keys:");
+ try {
+ pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
+ } catch (IOException e) {
+ pw.println("IOException: " + e);
+ }
+ }
+}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index a115345..ddecf14 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -114,6 +114,7 @@ public class UsbDeviceManager {
private boolean mAudioSourceEnabled;
private Map<String, List<Pair<String, String>>> mOemModeMap;
private String[] mAccessoryStrings;
+ private UsbDebuggingManager mDebuggingManager;
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
@@ -166,6 +167,10 @@ public class UsbDeviceManager {
if (DEBUG) Slog.d(TAG, "accessory attached at boot");
startAccessoryMode();
}
+
+ if ("1".equals(SystemProperties.get("ro.adb.secure"))) {
+ mDebuggingManager = new UsbDebuggingManager(context);
+ }
}
public void systemReady() {
@@ -425,6 +430,9 @@ public class UsbDeviceManager {
setEnabledFunctions(mDefaultFunctions, true);
updateAdbNotification();
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mAdbEnabled);
+ }
}
private void setEnabledFunctions(String functions, boolean makeDefault) {
@@ -601,6 +609,9 @@ public class UsbDeviceManager {
if (mCurrentAccessory != null) {
mSettingsManager.accessoryAttached(mCurrentAccessory);
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mAdbEnabled);
+ }
break;
}
}
@@ -802,10 +813,25 @@ public class UsbDeviceManager {
return usbFunctions;
}
+ public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
+ if (mDebuggingManager != null) {
+ mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey);
+ }
+ }
+
+ public void denyUsbDebugging() {
+ if (mDebuggingManager != null) {
+ mDebuggingManager.denyUsbDebugging();
+ }
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw) {
if (mHandler != null) {
mHandler.dump(fd, pw);
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.dump(fd, pw);
+ }
}
private native String[] nativeGetAccessoryStrings();
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 0205ef8..bebcd56 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -164,6 +164,16 @@ public class UsbService extends IUsbManager.Stub {
}
}
+ public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey);
+ }
+
+ public void denyUsbDebugging() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.denyUsbDebugging();
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index 13e8bc5..c25f010 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -10,10 +10,8 @@ import android.view.animation.Animation;
import android.view.animation.Transformation;
import java.io.PrintWriter;
+import java.util.ArrayList;
-/**
- *
- */
public class AppWindowAnimator {
static final String TAG = "AppWindowAnimator";
@@ -48,12 +46,15 @@ public class AppWindowAnimator {
Animation thumbnailAnimation;
final Transformation thumbnailTransformation = new Transformation();
+ /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */
+ ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<WindowStateAnimator>();
+
static final Animation sDummyAnimation = new DummyAnimation();
- public AppWindowAnimator(final WindowManagerService service, final AppWindowToken atoken) {
- mService = service;
+ public AppWindowAnimator(final AppWindowToken atoken) {
mAppToken = atoken;
- mAnimator = service.mAnimator;
+ mService = atoken.service;
+ mAnimator = atoken.mAnimator;
}
public void setAnimation(Animation anim, boolean initialized) {
@@ -123,7 +124,7 @@ public class AppWindowAnimator {
if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
mService.setInputMethodAnimLayerAdjustment(adj);
}
- if (w == mService.mWallpaperTarget && mService.mLowerWallpaperTarget == null) {
+ if (w == mAnimator.mWallpaperTarget && mAnimator.mLowerWallpaperTarget == null) {
mService.setWallpaperAnimLayerAdjustmentLocked(adj);
}
}
@@ -168,7 +169,7 @@ public class AppWindowAnimator {
}
transformation.clear();
final boolean more = animation.getTransformation(currentTime, transformation);
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ if (false && WindowManagerService.DEBUG_ANIM) Slog.v(
TAG, "Stepped animation in " + mAppToken + ": more=" + more + ", xform=" + transformation);
if (!more) {
animation = null;
@@ -255,9 +256,9 @@ public class AppWindowAnimator {
transformation.clear();
- final int N = mAppToken.windows.size();
+ final int N = mAllAppWinAnimators.size();
for (int i=0; i<N; i++) {
- mAppToken.windows.get(i).mWinAnimator.finishExit();
+ mAllAppWinAnimators.get(i).finishExit();
}
mAppToken.updateReportedVisibilityLocked();
@@ -266,9 +267,9 @@ public class AppWindowAnimator {
boolean showAllWindowsLocked() {
boolean isAnimating = false;
- final int NW = mAppToken.allAppWindows.size();
+ final int NW = mAllAppWinAnimators.size();
for (int i=0; i<NW; i++) {
- WindowStateAnimator winAnimator = mAppToken.allAppWindows.get(i).mWinAnimator;
+ WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
"performing show on: " + winAnimator);
winAnimator.performShowLocked();
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 6ecbb8e..13b072c 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -104,7 +104,7 @@ class AppWindowToken extends WindowToken {
appToken = _token;
mInputApplicationHandle = new InputApplicationHandle(this);
mAnimator = service.mAnimator;
- mAppAnimator = new AppWindowAnimator(_service, this);
+ mAppAnimator = new AppWindowAnimator(this);
}
void sendAppVisibilityToClients() {
diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java
index 27af313..64d2602 100644
--- a/services/java/com/android/server/wm/BlackFrame.java
+++ b/services/java/com/android/server/wm/BlackFrame.java
@@ -35,7 +35,7 @@ public class BlackFrame {
final int layer;
final Surface surface;
- BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b)
+ BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
throws Surface.OutOfResourcesException {
left = l;
top = t;
@@ -44,11 +44,11 @@ public class BlackFrame {
int h = b-t;
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
surface = new WindowStateAnimator.SurfaceTrace(session, 0, "BlackSurface("
- + l + ", " + t + ")",
- -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
+ + l + ", " + t + ")", layerStack,
+ w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
} else {
- surface = new Surface(session, 0, "BlackSurface",
- -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
+ surface = new Surface(session, 0, "BlackSurface", layerStack,
+ w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
}
surface.setAlpha(1);
surface.setLayer(layer);
@@ -103,7 +103,7 @@ public class BlackFrame {
}
public BlackFrame(SurfaceSession session, Rect outer, Rect inner,
- int layer) throws Surface.OutOfResourcesException {
+ int layer, final int layerStack) throws Surface.OutOfResourcesException {
boolean success = false;
mOuterRect = new Rect(outer);
@@ -111,19 +111,19 @@ public class BlackFrame {
try {
if (outer.top < inner.top) {
mBlackSurfaces[0] = new BlackSurface(session, layer,
- outer.left, outer.top, inner.right, inner.top);
+ outer.left, outer.top, inner.right, inner.top, layerStack);
}
if (outer.left < inner.left) {
mBlackSurfaces[1] = new BlackSurface(session, layer,
- outer.left, inner.top, inner.left, outer.bottom);
+ outer.left, inner.top, inner.left, outer.bottom, layerStack);
}
if (outer.bottom > inner.bottom) {
mBlackSurfaces[2] = new BlackSurface(session, layer,
- inner.left, inner.bottom, outer.right, outer.bottom);
+ inner.left, inner.bottom, outer.right, outer.bottom, layerStack);
}
if (outer.right > inner.right) {
mBlackSurfaces[3] = new BlackSurface(session, layer,
- inner.right, outer.top, outer.right, inner.bottom);
+ inner.right, outer.top, outer.right, inner.bottom, layerStack);
}
success = true;
} finally {
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index e8f56c8..81daac6 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -39,18 +39,18 @@ class DimAnimator {
int mLastDimWidth, mLastDimHeight;
- DimAnimator (SurfaceSession session) {
+ DimAnimator (SurfaceSession session, final int layerStack) {
if (mDimSurface == null) {
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0,
"DimAnimator",
- -1, 16, 16, PixelFormat.OPAQUE,
+ layerStack, 16, 16, PixelFormat.OPAQUE,
Surface.FX_SURFACE_DIM);
} else {
mDimSurface = new Surface(session, 0,
"DimAnimator",
- -1, 16, 16, PixelFormat.OPAQUE,
+ layerStack, 16, 16, PixelFormat.OPAQUE,
Surface.FX_SURFACE_DIM);
}
if (WindowManagerService.SHOW_TRANSACTIONS ||
diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java
index 9fca418..4ab8ce1 100644
--- a/services/java/com/android/server/wm/DimSurface.java
+++ b/services/java/com/android/server/wm/DimSurface.java
@@ -30,18 +30,18 @@ class DimSurface {
int mLayer = -1;
int mLastDimWidth, mLastDimHeight;
- DimSurface(SurfaceSession session) {
+ DimSurface(SurfaceSession session, final int layerStack) {
if (mDimSurface == null) {
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0,
"DimSurface",
- -1, 16, 16, PixelFormat.OPAQUE,
+ layerStack, 16, 16, PixelFormat.OPAQUE,
Surface.FX_SURFACE_DIM);
} else {
mDimSurface = new Surface(session, 0,
"DimSurface",
- -1, 16, 16, PixelFormat.OPAQUE,
+ layerStack, 16, 16, PixelFormat.OPAQUE,
Surface.FX_SURFACE_DIM);
}
if (WindowManagerService.SHOW_TRANSACTIONS ||
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
new file mode 100644
index 0000000..6e5bbcb
--- /dev/null
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class DisplayContentList extends ArrayList<DisplayContent> {
+}
+
+/**
+ * Utility class for keeping track of the WindowStates and other pertinent contents of a
+ * particular Display.
+ *
+ * IMPORTANT: No method from this class should ever be used without holding
+ * WindowManagerService.mWindowMap.
+ */
+class DisplayContent {
+
+ /** Unique identifier of this stack. */
+ private final int mDisplayId;
+
+ /** Z-ordered (bottom-most first) list of all Window objects. Assigned to an element
+ * from mDisplayWindows; */
+ private WindowList mWindows = new WindowList();
+
+
+ // This protects the following display size properties, so that
+ // getDisplaySize() doesn't need to acquire the global lock. This is
+ // needed because the window manager sometimes needs to use ActivityThread
+ // while it has its global state locked (for example to load animation
+ // resources), but the ActivityThread also needs get the current display
+ // size sometimes when it has its package lock held.
+ //
+ // These will only be modified with both mWindowMap and mDisplaySizeLock
+ // held (in that order) so the window manager doesn't need to acquire this
+ // lock when needing these values in its normal operation.
+ final Object mDisplaySizeLock = new Object();
+ int mInitialDisplayWidth = 0;
+ int mInitialDisplayHeight = 0;
+ int mInitialDisplayDensity = 0;
+ int mBaseDisplayWidth = 0;
+ int mBaseDisplayHeight = 0;
+ int mBaseDisplayDensity = 0;
+ final DisplayInfo mDisplayInfo = new DisplayInfo();
+ final Display mDisplay;
+
+ DisplayContent(Display display) {
+ mDisplay = display;
+ mDisplayId = display.getDisplayId();
+ display.getDisplayInfo(mDisplayInfo);
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ WindowList getWindowList() {
+ return mWindows;
+ }
+
+ Display getDisplay() {
+ return mDisplay;
+ }
+
+ DisplayInfo getDisplayInfo() {
+ return mDisplayInfo;
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.print(" Display: mDisplayId="); pw.println(mDisplayId);
+ pw.print(" init="); pw.print(mInitialDisplayWidth); pw.print("x");
+ pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
+ pw.print("dpi");
+ if (mInitialDisplayWidth != mBaseDisplayWidth
+ || mInitialDisplayHeight != mBaseDisplayHeight
+ || mInitialDisplayDensity != mBaseDisplayDensity) {
+ pw.print(" base=");
+ pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
+ pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
+ }
+ pw.print(" cur=");
+ pw.print(mDisplayInfo.logicalWidth);
+ pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
+ pw.print(" app=");
+ pw.print(mDisplayInfo.appWidth);
+ pw.print("x"); pw.print(mDisplayInfo.appHeight);
+ pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth);
+ pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
+ pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
+ pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
+ pw.println();
+ }
+}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index b2cf3e0..3fcf680 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -29,6 +29,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.DisplayInfo;
import android.view.DragEvent;
import android.view.InputChannel;
import android.view.Surface;
@@ -58,6 +59,7 @@ class DragState {
WindowState mTargetWindow;
ArrayList<WindowState> mNotifiedWindows;
boolean mDragInProgress;
+ DisplayContent mDisplayContent;
private final Region mTmpRegion = new Region();
@@ -84,7 +86,11 @@ class DragState {
mNotifiedWindows = null;
}
- void register() {
+ /**
+ * @param displayContent The display parameters associated with the window being dragged.
+ */
+ void register(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
if (mClientChannel != null) {
Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
@@ -101,7 +107,8 @@ class DragState {
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null);
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+ mDisplayContent.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.inputChannel = mServerChannel;
mDragWindowHandle.layer = getDragLayerLw();
@@ -125,8 +132,9 @@ class DragState {
// The drag window covers the entire display
mDragWindowHandle.frameLeft = 0;
mDragWindowHandle.frameTop = 0;
- mDragWindowHandle.frameRight = mService.mCurDisplayWidth;
- mDragWindowHandle.frameBottom = mService.mCurDisplayHeight;
+ DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+ mDragWindowHandle.frameRight = displayInfo.logicalWidth;
+ mDragWindowHandle.frameBottom = displayInfo.logicalHeight;
// Pause rotations before a drag.
if (WindowManagerService.DEBUG_ORIENTATION) {
@@ -179,9 +187,10 @@ class DragState {
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
- final int N = mService.mWindows.size();
+ final WindowList windows = mDisplayContent.getWindowList();
+ final int N = windows.size();
for (int i = 0; i < N; i++) {
- sendDragStartedLw(mService.mWindows.get(i), touchX, touchY, mDataDescription);
+ sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
}
}
@@ -380,7 +389,8 @@ class DragState {
WindowState touchedWin = null;
final int x = (int) xf;
final int y = (int) yf;
- final ArrayList<WindowState> windows = mService.mWindows;
+
+ final WindowList windows = mDisplayContent.getWindowList();
final int N = windows.size();
for (int i = N - 1; i >= 0; i--) {
WindowState child = windows.get(i);
diff --git a/services/java/com/android/server/wm/FakeWindowImpl.java b/services/java/com/android/server/wm/FakeWindowImpl.java
index 2527f46..5ec72cc 100644
--- a/services/java/com/android/server/wm/FakeWindowImpl.java
+++ b/services/java/com/android/server/wm/FakeWindowImpl.java
@@ -22,6 +22,7 @@ import com.android.server.input.InputWindowHandle;
import android.os.Looper;
import android.os.Process;
import android.util.Slog;
+import android.view.Display;
import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.InputQueue;
@@ -56,7 +57,7 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
mApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mWindowHandle = new InputWindowHandle(mApplicationHandle, null);
+ mWindowHandle = new InputWindowHandle(mApplicationHandle, null, Display.DEFAULT_DISPLAY);
mWindowHandle.name = name;
mWindowHandle.inputChannel = mServerChannel;
mWindowLayer = getLayerLw(windowType);
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 285d230..5ff8a9b 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -128,7 +128,7 @@ final class InputMonitor implements InputManagerService.Callbacks {
return 0; // abort dispatching
}
- private void addInputWindowHandleLw(InputWindowHandle windowHandle) {
+ private void addInputWindowHandleLw(final InputWindowHandle windowHandle) {
if (mInputWindowHandles == null) {
mInputWindowHandles = new InputWindowHandle[16];
}
@@ -139,6 +139,44 @@ final class InputMonitor implements InputManagerService.Callbacks {
mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
}
+ private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
+ final WindowState child, final int flags, final int type,
+ final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper) {
+ // Add a window to our list of input windows.
+ inputWindowHandle.name = child.toString();
+ inputWindowHandle.layoutParamsFlags = flags;
+ inputWindowHandle.layoutParamsType = type;
+ inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
+ inputWindowHandle.visible = isVisible;
+ inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
+ inputWindowHandle.hasFocus = hasFocus;
+ inputWindowHandle.hasWallpaper = hasWallpaper;
+ inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindowHandle.layer = child.mLayer;
+ inputWindowHandle.ownerPid = child.mSession.mPid;
+ inputWindowHandle.ownerUid = child.mSession.mUid;
+ inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
+
+ final Rect frame = child.mFrame;
+ inputWindowHandle.frameLeft = frame.left;
+ inputWindowHandle.frameTop = frame.top;
+ inputWindowHandle.frameRight = frame.right;
+ inputWindowHandle.frameBottom = frame.bottom;
+
+ if (child.mGlobalScale != 1) {
+ // If we are scaling the window, input coordinates need
+ // to be inversely scaled to map from what is on screen
+ // to what is actually being touched in the UI.
+ inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
+ } else {
+ inputWindowHandle.scaleFactor = 1;
+ }
+
+ child.getTouchableRegion(inputWindowHandle.touchableRegion);
+
+ addInputWindowHandleLw(inputWindowHandle);
+ }
+
private void clearInputWindowHandlesLw() {
while (mInputWindowHandleCount != 0) {
mInputWindowHandles[--mInputWindowHandleCount] = null;
@@ -163,7 +201,9 @@ final class InputMonitor implements InputManagerService.Callbacks {
// As an optimization, we could try to prune the list of windows but this turns
// out to be difficult because only the native code knows for sure which window
// currently has touch focus.
- final ArrayList<WindowState> windows = mService.mWindows;
+ final WindowStateAnimator universeBackground = mService.mAnimator.mUniverseBackground;
+ final int aboveUniverseLayer = mService.mAnimator.mAboveUniverseLayer;
+ boolean addedUniverse = false;
// If there's a drag in flight, provide a pseudowindow to catch drag input
final boolean inDrag = (mService.mDragState != null);
@@ -185,8 +225,9 @@ final class InputMonitor implements InputManagerService.Callbacks {
addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
}
- final int N = windows.size();
- for (int i = N - 1; i >= 0; i--) {
+ // TODO(multidisplay): Input only occurs on the default display.
+ final WindowList windows = mService.getDefaultWindowList();
+ for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState child = windows.get(i);
final InputChannel inputChannel = child.mInputChannel;
final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
@@ -209,39 +250,20 @@ final class InputMonitor implements InputManagerService.Callbacks {
mService.mDragState.sendDragStartedIfNeededLw(child);
}
- // Add a window to our list of input windows.
- inputWindowHandle.name = child.toString();
- inputWindowHandle.layoutParamsFlags = flags;
- inputWindowHandle.layoutParamsType = type;
- inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
- inputWindowHandle.visible = isVisible;
- inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
- inputWindowHandle.hasFocus = hasFocus;
- inputWindowHandle.hasWallpaper = hasWallpaper;
- inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
- inputWindowHandle.layer = child.mLayer;
- inputWindowHandle.ownerPid = child.mSession.mPid;
- inputWindowHandle.ownerUid = child.mSession.mUid;
- inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
-
- final Rect frame = child.mFrame;
- inputWindowHandle.frameLeft = frame.left;
- inputWindowHandle.frameTop = frame.top;
- inputWindowHandle.frameRight = frame.right;
- inputWindowHandle.frameBottom = frame.bottom;
-
- if (child.mGlobalScale != 1) {
- // If we are scaling the window, input coordinates need
- // to be inversely scaled to map from what is on screen
- // to what is actually being touched in the UI.
- inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
- } else {
- inputWindowHandle.scaleFactor = 1;
+ if (universeBackground != null && !addedUniverse
+ && child.mBaseLayer < aboveUniverseLayer) {
+ final WindowState u = universeBackground.mWin;
+ if (u.mInputChannel != null && u.mInputWindowHandle != null) {
+ addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
+ u.mAttrs.type, true, u == mInputFocus, false);
+ }
+ addedUniverse = true;
}
- child.getTouchableRegion(inputWindowHandle.touchableRegion);
-
- addInputWindowHandleLw(inputWindowHandle);
+ if (child.mWinAnimator != universeBackground) {
+ addInputWindowHandleLw(inputWindowHandle, child, flags, type,
+ isVisible, hasFocus, hasWallpaper);
+ }
}
// Send windows to native code.
diff --git a/services/java/com/android/server/wm/KeyguardDisableHandler.java b/services/java/com/android/server/wm/KeyguardDisableHandler.java
new file mode 100644
index 0000000..d935b8b
--- /dev/null
+++ b/services/java/com/android/server/wm/KeyguardDisableHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.TokenWatcher;
+import android.util.Log;
+import android.util.Pair;
+import android.view.WindowManagerPolicy;
+
+public class KeyguardDisableHandler extends Handler {
+ private static final String TAG = "KeyguardDisableHandler";
+
+ private static final int ALLOW_DISABLE_YES = 1;
+ private static final int ALLOW_DISABLE_NO = 0;
+ private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager
+ private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher
+
+ // Message.what constants
+ static final int KEYGUARD_DISABLE = 1;
+ static final int KEYGUARD_REENABLE = 2;
+ static final int KEYGUARD_POLICY_CHANGED = 3;
+
+ final Context mContext;
+ final WindowManagerPolicy mPolicy;
+ KeyguardTokenWatcher mKeyguardTokenWatcher;
+
+ public KeyguardDisableHandler(final Context context, final WindowManagerPolicy policy) {
+ mContext = context;
+ mPolicy = policy;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void handleMessage(Message msg) {
+ if (mKeyguardTokenWatcher == null) {
+ mKeyguardTokenWatcher = new KeyguardTokenWatcher(this);
+ }
+
+ switch (msg.what) {
+ case KEYGUARD_DISABLE:
+ final Pair<IBinder, String> pair = (Pair<IBinder, String>)msg.obj;
+ mKeyguardTokenWatcher.acquire(pair.first, pair.second);
+ break;
+
+ case KEYGUARD_REENABLE:
+ mKeyguardTokenWatcher.release((IBinder)msg.obj);
+ break;
+
+ case KEYGUARD_POLICY_CHANGED:
+ mPolicy.enableKeyguard(true);
+ // lazily evaluate this next time we're asked to disable keyguard
+ mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN;
+ break;
+ }
+ }
+
+ class KeyguardTokenWatcher extends TokenWatcher {
+
+ public KeyguardTokenWatcher(final Handler handler) {
+ super(handler, TAG);
+ }
+
+ @Override
+ public void acquired() {
+ // We fail safe and prevent disabling keyguard in the unlikely event this gets
+ // called before DevicePolicyManagerService has started.
+ if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) {
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ mAllowDisableKeyguard = dpm.getPasswordQuality(null)
+ == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ?
+ ALLOW_DISABLE_YES : ALLOW_DISABLE_NO;
+ }
+ }
+ if (mAllowDisableKeyguard == ALLOW_DISABLE_YES) {
+ mPolicy.enableKeyguard(false);
+ } else {
+ Log.v(TAG, "Not disabling keyguard since device policy is enforced");
+ }
+ }
+
+ @Override
+ public void released() {
+ mPolicy.enableKeyguard(true);
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 938db9e..7679413 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -25,6 +25,7 @@ import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.Slog;
+import android.view.Display;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.animation.Animation;
@@ -41,6 +42,7 @@ class ScreenRotationAnimation {
static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
final Context mContext;
+ final Display mDisplay;
Surface mSurface;
BlackFrame mCustomBlackFrame;
BlackFrame mExitingBlackFrame;
@@ -185,9 +187,10 @@ class ScreenRotationAnimation {
pw.println();
}
- public ScreenRotationAnimation(Context context, SurfaceSession session,
+ public ScreenRotationAnimation(Context context, Display display, SurfaceSession session,
boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) {
mContext = context;
+ mDisplay = display;
// Screenshot does NOT include rotation!
if (originalRotation == Surface.ROTATION_90
@@ -212,11 +215,13 @@ class ScreenRotationAnimation {
try {
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- mSurface = new SurfaceTrace(session, 0, "FreezeSurface", -1, mWidth, mHeight,
- PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
+ mSurface = new SurfaceTrace(session, 0, "FreezeSurface",
+ mDisplay.getLayerStack(), mWidth, mHeight,
+ PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
} else {
- mSurface = new Surface(session, 0, "FreezeSurface", -1, mWidth, mHeight,
- PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
+ mSurface = new Surface(session, 0, "FreezeSurface",
+ mDisplay.getLayerStack(), mWidth, mHeight,
+ PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
}
if (!mSurface.isValid()) {
// Screenshot failed, punt.
@@ -472,6 +477,7 @@ class ScreenRotationAnimation {
mRotateFrameAnimation.scaleCurrentDuration(animationScale);
}
+ final int layerStack = mDisplay.getLayerStack();
if (USE_CUSTOM_BLACK_FRAME && mCustomBlackFrame == null) {
if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
WindowManagerService.TAG,
@@ -490,7 +496,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
- mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3);
+ mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3,
+ layerStack);
mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -520,7 +527,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
- mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2);
+ mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2,
+ layerStack);
mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -542,7 +550,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
finalWidth*2, finalHeight*2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
+ mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER,
+ layerStack);
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 61c0e9c..1ffbecc 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -33,6 +33,7 @@ import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
+import android.view.Display;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.InputChannel;
@@ -134,15 +135,33 @@ final class Session extends IWindowSession.Stub
}
}
+ @Override
public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, outContentInsets,
- outInputChannel);
+ return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
+ outContentInsets, outInputChannel);
}
-
+
+ @Override
+ public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ int viewVisibility, int displayId, Rect outContentInsets,
+ InputChannel outInputChannel) {
+ return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
+ outContentInsets, outInputChannel);
+ }
+
+ @Override
public int addWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, outContentInsets, null);
+ return addToDisplayWithoutInputChannel(window, seq, attrs, viewVisibility,
+ Display.DEFAULT_DISPLAY, outContentInsets);
+ }
+
+ @Override
+ public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ int viewVisibility, int displayId, Rect outContentInsets) {
+ return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
+ outContentInsets, null);
}
public void remove(IWindow window) {
@@ -261,7 +280,7 @@ final class Session extends IWindowSession.Stub
// !!! FIXME: put all this heavy stuff onto the mH looper, as well as
// the actual drag event dispatch stuff in the dragstate
- mService.mDragState.register();
+ mService.mDragState.register(callingWin.mDisplayContent);
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mService.mDragState.mServerChannel)) {
@@ -389,6 +408,20 @@ final class Session extends IWindowSession.Stub
mService.wallpaperCommandComplete(window, result);
}
+ public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
+ float dsdx, float dtdx, float dsdy, float dtdy) {
+ synchronized(mService.mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mService.setUniverseTransformLocked(
+ mService.windowForClientLocked(this, window, true),
+ alpha, offx, offy, dsdx, dtdx, dsdy, dtdy);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
diff --git a/services/java/com/android/server/wm/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java
index 768d2db..775aa0f 100644
--- a/services/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/java/com/android/server/wm/StrictModeFlash.java
@@ -22,8 +22,6 @@ import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
-import android.util.DisplayMetrics;
-import android.util.Slog;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceSession;
@@ -39,7 +37,8 @@ class StrictModeFlash {
public StrictModeFlash(Display display, SurfaceSession session) {
try {
- mSurface = new Surface(session, 0, "StrictModeFlash", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+ mSurface = new Surface(session, 0, "StrictModeFlash", display.getLayerStack(),
+ 1, 1, PixelFormat.TRANSLUCENT, 0);
} catch (Surface.OutOfResourcesException e) {
return;
}
diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java
index 5497eb4..5901cc8 100644
--- a/services/java/com/android/server/wm/Watermark.java
+++ b/services/java/com/android/server/wm/Watermark.java
@@ -35,6 +35,7 @@ import android.view.Surface.OutOfResourcesException;
* Displays a watermark on top of the window manager's windows.
*/
class Watermark {
+ final Display mDisplay;
final String[] mTokens;
final String mText;
final Paint mTextPaint;
@@ -50,7 +51,7 @@ class Watermark {
int mLastDH;
boolean mDrawNeeded;
- Watermark(DisplayMetrics dm, SurfaceSession session, String[] tokens) {
+ Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) {
if (false) {
Log.i(WindowManagerService.TAG, "*********************** WATERMARK");
for (int i=0; i<tokens.length; i++) {
@@ -58,6 +59,7 @@ class Watermark {
}
}
+ mDisplay = display;
mTokens = tokens;
StringBuilder builder = new StringBuilder(32);
@@ -112,7 +114,8 @@ class Watermark {
try {
mSurface = new Surface(session, 0,
- "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+ "WatermarkSurface", mDisplay.getLayerStack(),
+ 1, 1, PixelFormat.TRANSLUCENT, 0);
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100);
mSurface.setPosition(0, 0);
mSurface.show();
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 62cf711..580f00d 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -8,19 +8,22 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED;
+import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowManagerService.H.SET_DIM_PARAMETERS;
+import static com.android.server.wm.WindowManagerService.H.UPDATE_ANIM_PARAMETERS;
import android.content.Context;
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
+import android.view.Display;
import android.view.Surface;
-import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
import com.android.internal.policy.impl.PhoneWindowManager;
+import com.android.server.wm.WindowManagerService.AppWindowAnimParams;
+import com.android.server.wm.WindowManagerService.LayoutToAnimatorParams;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -32,22 +35,16 @@ import java.util.ArrayList;
public class WindowAnimator {
private static final String TAG = "WindowAnimator";
- // mForceHiding states.
- private static final int KEYGUARD_NOT_SHOWN = 0;
- private static final int KEYGUARD_ANIMATING_IN = 1;
- private static final int KEYGUARD_SHOWN = 2;
- private static final int KEYGUARD_ANIMATING_OUT = 3;
- int mForceHiding;
-
final WindowManagerService mService;
final Context mContext;
final WindowManagerPolicy mPolicy;
- ArrayList<WindowStateAnimator> mWinAnimators = new ArrayList<WindowStateAnimator>();
+ ArrayList<WinAnimatorList> mWinAnimatorLists = new ArrayList<WinAnimatorList>();
boolean mAnimating;
- WindowState mWindowAnimationBackground;
- int mWindowAnimationBackgroundColor;
+
+ final Runnable mAnimationRunnable;
+
int mAdjResult;
int mPendingLayoutChanges;
@@ -71,11 +68,14 @@ public class WindowAnimator {
// Window currently running an animation that has requested it be detached
// from the wallpaper. This means we need to ensure the wallpaper is
// visible behind it in case it animates in a way that would allow it to be
- // seen.
+ // seen. If multiple windows satisfy this, use the lowest window.
WindowState mWindowDetachedWallpaper = null;
- WindowState mDetachedWallpaper = null;
+
DimSurface mWindowAnimationBackgroundSurface = null;
+ WindowStateAnimator mUniverseBackground = null;
+ int mAboveUniverseLayer = 0;
+
int mBulkUpdateParams = 0;
DimAnimator mDimAnimator = null;
@@ -84,75 +84,149 @@ public class WindowAnimator {
static final int WALLPAPER_ACTION_PENDING = 1;
int mPendingActions;
- WindowAnimator(final WindowManagerService service, final Context context,
- final WindowManagerPolicy policy) {
- mService = service;
- mContext = context;
- mPolicy = policy;
+ WindowState mWallpaperTarget = null;
+ AppWindowAnimator mWpAppAnimator = null;
+ WindowState mLowerWallpaperTarget = null;
+ WindowState mUpperWallpaperTarget = null;
+
+ ArrayList<AppWindowAnimator> mAppAnimators = new ArrayList<AppWindowAnimator>();
+
+ ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
+
+ /** Parameters being passed from this into mService. */
+ static class AnimatorToLayoutParams {
+ boolean mUpdateQueued;
+ int mBulkUpdateParams;
+ int mPendingLayoutChanges;
+ WindowState mWindowDetachedWallpaper;
}
+ /** Do not modify unless holding mService.mWindowMap or this and mAnimToLayout in that order */
+ final AnimatorToLayoutParams mAnimToLayout = new AnimatorToLayoutParams();
- void hideWallpapersLocked(final WindowState w) {
- if ((mService.mWallpaperTarget == w && mService.mLowerWallpaperTarget == null)
- || mService.mWallpaperTarget == null) {
- for (final WindowToken token : mService.mWallpaperTokens) {
- for (final WindowState wallpaper : token.windows) {
- final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
- if (!winAnimator.mLastHidden) {
- winAnimator.hide();
- mService.dispatchWallpaperVisibility(wallpaper, false);
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ boolean mInitialized = false;
+
+ WindowAnimator(final WindowManagerService service) {
+ mService = service;
+ mContext = service.mContext;
+ mPolicy = service.mPolicy;
+
+ mAnimationRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // TODO(cmautner): When full isolation is achieved for animation, the first lock
+ // goes away and only the WindowAnimator.this remains.
+ synchronized(mService.mWindowMap) {
+ synchronized(WindowAnimator.this) {
+ copyLayoutToAnimParamsLocked();
+ animateLocked();
}
}
- token.hidden = true;
+ }
+ };
+ }
+
+ void initializeLocked(final int layerStack) {
+ mWindowAnimationBackgroundSurface =
+ new DimSurface(mService.mFxSession, layerStack);
+ mDimAnimator = new DimAnimator(mService.mFxSession, layerStack);
+ mInitialized = true;
+ }
+
+ /** Locked on mAnimToLayout */
+ void updateAnimToLayoutLocked() {
+ final AnimatorToLayoutParams animToLayout = mAnimToLayout;
+ synchronized (animToLayout) {
+ animToLayout.mBulkUpdateParams = mBulkUpdateParams;
+ animToLayout.mPendingLayoutChanges = mPendingLayoutChanges;
+ animToLayout.mWindowDetachedWallpaper = mWindowDetachedWallpaper;
+
+ if (!animToLayout.mUpdateQueued) {
+ animToLayout.mUpdateQueued = true;
+ mService.mH.sendMessage(mService.mH.obtainMessage(UPDATE_ANIM_PARAMETERS));
}
}
}
- private void testWallpaperAndBackgroundLocked() {
- if (mWindowDetachedWallpaper != mDetachedWallpaper) {
- if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
- "Detached wallpaper changed from " + mWindowDetachedWallpaper
- + " to " + mDetachedWallpaper);
- mWindowDetachedWallpaper = mDetachedWallpaper;
- mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
+ /** Copy all WindowManagerService params into local params here. Locked on 'this'. */
+ private void copyLayoutToAnimParamsLocked() {
+ final LayoutToAnimatorParams layoutToAnim = mService.mLayoutToAnim;
+ synchronized(layoutToAnim) {
+ layoutToAnim.mAnimationScheduled = false;
+
+ if (!layoutToAnim.mParamsModified) {
+ return;
+ }
+ layoutToAnim.mParamsModified = false;
+
+ if ((layoutToAnim.mChanges & LayoutToAnimatorParams.WALLPAPER_TOKENS_CHANGED) != 0) {
+ layoutToAnim.mChanges &= ~LayoutToAnimatorParams.WALLPAPER_TOKENS_CHANGED;
+ mWallpaperTokens = new ArrayList<WindowToken>(layoutToAnim.mWallpaperTokens);
+ }
+
+ mWinAnimatorLists =
+ new ArrayList<WinAnimatorList>(layoutToAnim.mWinAnimatorLists);
+ mWallpaperTarget = layoutToAnim.mWallpaperTarget;
+ mWpAppAnimator = mWallpaperTarget == null
+ ? null : mWallpaperTarget.mAppToken == null
+ ? null : mWallpaperTarget.mAppToken.mAppAnimator;
+ mLowerWallpaperTarget = layoutToAnim.mLowerWallpaperTarget;
+ mUpperWallpaperTarget = layoutToAnim.mUpperWallpaperTarget;
+
+ // Set the new DimAnimator params.
+ DimAnimator.Parameters dimParams = layoutToAnim.mDimParams;
+ if (dimParams == null) {
+ mDimParams = dimParams;
+ } else {
+ final WindowStateAnimator newWinAnimator = dimParams.mDimWinAnimator;
+
+ // Only set dim params on the highest dimmed layer.
+ final WindowStateAnimator existingDimWinAnimator = mDimParams == null
+ ? null : mDimParams.mDimWinAnimator;
+ // Don't turn on for an unshown surface, or for any layer but the highest dimmed one.
+ if (newWinAnimator.mSurfaceShown &&
+ (existingDimWinAnimator == null || !existingDimWinAnimator.mSurfaceShown
+ || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
+ mDimParams = dimParams;
+ }
+ }
+
+ mAppAnimators.clear();
+ final int N = layoutToAnim.mAppWindowAnimParams.size();
+ for (int i = 0; i < N; i++) {
+ final AppWindowAnimParams params = layoutToAnim.mAppWindowAnimParams.get(i);
+ AppWindowAnimator appAnimator = params.mAppAnimator;
+ appAnimator.mAllAppWinAnimators.clear();
+ appAnimator.mAllAppWinAnimators.addAll(params.mWinAnimators);
+ mAppAnimators.add(appAnimator);
+ }
}
+ }
- if (mWindowAnimationBackgroundColor != 0) {
- // If the window that wants black is the current wallpaper
- // target, then the black goes *below* the wallpaper so we
- // don't cause the wallpaper to suddenly disappear.
- WindowState target = mWindowAnimationBackground;
- if (mService.mWallpaperTarget == target
- || mService.mLowerWallpaperTarget == target
- || mService.mUpperWallpaperTarget == target) {
- final int N = mService.mWindows.size();
- for (int i = 0; i < N; i++) {
- WindowState w = mService.mWindows.get(i);
- if (w.mIsWallpaper) {
- target = w;
- break;
+ void hideWallpapersLocked(final WindowState w) {
+ if ((mWallpaperTarget == w && mLowerWallpaperTarget == null) || mWallpaperTarget == null) {
+ final int numTokens = mWallpaperTokens.size();
+ for (int i = numTokens - 1; i >= 0; i--) {
+ final WindowToken token = mWallpaperTokens.get(i);
+ final int numWindows = token.windows.size();
+ for (int j = numWindows - 1; j >= 0; j--) {
+ final WindowState wallpaper = token.windows.get(j);
+ final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
+ if (!winAnimator.mLastHidden) {
+ winAnimator.hide();
+ mService.dispatchWallpaperVisibility(wallpaper, false);
+ mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
}
+ token.hidden = true;
}
- if (mWindowAnimationBackgroundSurface == null) {
- mWindowAnimationBackgroundSurface = new DimSurface(mService.mFxSession);
- }
- final int dw = mDw;
- final int dh = mDh;
- mWindowAnimationBackgroundSurface.show(dw, dh,
- target.mWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM,
- mWindowAnimationBackgroundColor);
- } else if (mWindowAnimationBackgroundSurface != null) {
- mWindowAnimationBackgroundSurface.hide();
}
}
private void updateWindowsAppsAndRotationAnimationsLocked() {
- final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens;
int i;
- final int NAT = appTokens.size();
+ final int NAT = mAppAnimators.size();
for (i=0; i<NAT; i++) {
- final AppWindowAnimator appAnimator = appTokens.get(i).mAppAnimator;
+ final AppWindowAnimator appAnimator = mAppAnimators.get(i);
final boolean wasAnimating = appAnimator.animation != null
&& appAnimator.animation != AppWindowAnimator.sDummyAnimation;
if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
@@ -199,15 +273,22 @@ public class WindowAnimator {
}
}
- private void updateWindowsAndWallpaperLocked() {
+ private void updateWindowsLocked(final WinAnimatorList winAnimatorList) {
++mAnimTransactionSequence;
ArrayList<WindowStateAnimator> unForceHiding = null;
boolean wallpaperInUnForceHiding = false;
- for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
- WindowState win = mService.mWindows.get(i);
- WindowStateAnimator winAnimator = win.mWinAnimator;
+ // forceHiding states.
+ final int KEYGUARD_NOT_SHOWN = 0;
+ final int KEYGUARD_ANIMATING_IN = 1;
+ final int KEYGUARD_SHOWN = 2;
+ final int KEYGUARD_ANIMATING_OUT = 3;
+ int forceHiding = KEYGUARD_NOT_SHOWN;
+
+ for (int i = winAnimatorList.size() - 1; i >= 0; i--) {
+ WindowStateAnimator winAnimator = winAnimatorList.get(i);
+ WindowState win = winAnimator.mWin;
final int flags = winAnimator.mAttrFlags;
if (winAnimator.mSurface != null) {
@@ -219,51 +300,7 @@ public class WindowAnimator {
", nowAnimating=" + nowAnimating);
}
- // If this window is animating, make a note that we have
- // an animating window and take care of a request to run
- // a detached wallpaper animation.
- if (nowAnimating) {
- if (winAnimator.mAnimation != null) {
- if ((flags & FLAG_SHOW_WALLPAPER) != 0
- && winAnimator.mAnimation.getDetachWallpaper()) {
- mDetachedWallpaper = win;
- }
- final int backgroundColor = winAnimator.mAnimation.getBackgroundColor();
- if (backgroundColor != 0) {
- if (mWindowAnimationBackground == null
- || (winAnimator.mAnimLayer <
- mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
- mWindowAnimationBackground = win;
- mWindowAnimationBackgroundColor = backgroundColor;
- }
- }
- }
- mAnimating = true;
- }
-
- // If this window's app token is running a detached wallpaper
- // animation, make a note so we can ensure the wallpaper is
- // displayed behind it.
- final AppWindowAnimator appAnimator =
- win.mAppToken == null ? null : win.mAppToken.mAppAnimator;
- if (appAnimator != null && appAnimator.animation != null
- && appAnimator.animating) {
- if ((flags & FLAG_SHOW_WALLPAPER) != 0
- && appAnimator.animation.getDetachWallpaper()) {
- mDetachedWallpaper = win;
- }
- final int backgroundColor = appAnimator.animation.getBackgroundColor();
- if (backgroundColor != 0) {
- if (mWindowAnimationBackground == null
- || (winAnimator.mAnimLayer <
- mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
- mWindowAnimationBackground = win;
- mWindowAnimationBackgroundColor = backgroundColor;
- }
- }
- }
-
- if (wasAnimating && !winAnimator.mAnimating && mService.mWallpaperTarget == win) {
+ if (wasAnimating && !winAnimator.mAnimating && mWallpaperTarget == win) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
@@ -288,16 +325,16 @@ public class WindowAnimator {
if (win.isReadyForDisplay()) {
if (nowAnimating) {
if (winAnimator.mAnimationIsEntrance) {
- mForceHiding = KEYGUARD_ANIMATING_IN;
+ forceHiding = KEYGUARD_ANIMATING_IN;
} else {
- mForceHiding = KEYGUARD_ANIMATING_OUT;
+ forceHiding = KEYGUARD_ANIMATING_OUT;
}
} else {
- mForceHiding = KEYGUARD_SHOWN;
+ forceHiding = KEYGUARD_SHOWN;
}
}
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
- "Force hide " + mForceHiding
+ "Force hide " + forceHiding
+ " hasSurface=" + win.mHasSurface
+ " policyVis=" + win.mPolicyVisibility
+ " destroying=" + win.mDestroying
@@ -309,9 +346,9 @@ public class WindowAnimator {
final boolean hideWhenLocked =
(winAnimator.mAttrFlags & FLAG_SHOW_WHEN_LOCKED) == 0;
final boolean changed;
- if (((mForceHiding == KEYGUARD_ANIMATING_IN)
+ if (((forceHiding == KEYGUARD_ANIMATING_IN)
&& (!winAnimator.isAnimating() || hideWhenLocked))
- || ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) {
+ || ((forceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) {
changed = win.hideLw(false, false);
if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
"Now policy hidden: " + win);
@@ -326,7 +363,7 @@ public class WindowAnimator {
unForceHiding = new ArrayList<WindowStateAnimator>();
}
unForceHiding.add(winAnimator);
- if ((win.mAttrs.flags&WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
wallpaperInUnForceHiding = true;
}
}
@@ -338,7 +375,7 @@ public class WindowAnimator {
}
}
}
- if (changed && (flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if (changed && (flags & FLAG_SHOW_WALLPAPER) != 0) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
@@ -361,8 +398,7 @@ public class WindowAnimator {
}
}
}
- final AppWindowAnimator appAnimator =
- atoken == null ? null : atoken.mAppAnimator;
+ final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
if (appAnimator != null && appAnimator.thumbnail != null) {
if (appAnimator.thumbnailTransactionSeq != mAnimTransactionSequence) {
appAnimator.thumbnailTransactionSeq = mAnimTransactionSequence;
@@ -388,21 +424,112 @@ public class WindowAnimator {
}
}
+ private void updateWallpaperLocked(final WinAnimatorList winAnimatorList) {
+ WindowStateAnimator windowAnimationBackground = null;
+ int windowAnimationBackgroundColor = 0;
+ WindowState detachedWallpaper = null;
+
+ for (int i = winAnimatorList.size() - 1; i >= 0; i--) {
+ WindowStateAnimator winAnimator = winAnimatorList.get(i);
+ if (winAnimator.mSurface == null) {
+ continue;
+ }
+
+ final int flags = winAnimator.mAttrFlags;
+ final WindowState win = winAnimator.mWin;
+
+ // If this window is animating, make a note that we have
+ // an animating window and take care of a request to run
+ // a detached wallpaper animation.
+ if (winAnimator.mAnimating) {
+ if (winAnimator.mAnimation != null) {
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0
+ && winAnimator.mAnimation.getDetachWallpaper()) {
+ detachedWallpaper = win;
+ }
+ final int backgroundColor = winAnimator.mAnimation.getBackgroundColor();
+ if (backgroundColor != 0) {
+ if (windowAnimationBackground == null || (winAnimator.mAnimLayer <
+ windowAnimationBackground.mAnimLayer)) {
+ windowAnimationBackground = winAnimator;
+ windowAnimationBackgroundColor = backgroundColor;
+ }
+ }
+ }
+ mAnimating = true;
+ }
+
+ // If this window's app token is running a detached wallpaper
+ // animation, make a note so we can ensure the wallpaper is
+ // displayed behind it.
+ final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
+ if (appAnimator != null && appAnimator.animation != null
+ && appAnimator.animating) {
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0
+ && appAnimator.animation.getDetachWallpaper()) {
+ detachedWallpaper = win;
+ }
+
+ final int backgroundColor = appAnimator.animation.getBackgroundColor();
+ if (backgroundColor != 0) {
+ if (windowAnimationBackground == null || (winAnimator.mAnimLayer <
+ windowAnimationBackground.mAnimLayer)) {
+ windowAnimationBackground = winAnimator;
+ windowAnimationBackgroundColor = backgroundColor;
+ }
+ }
+ }
+ } // end forall windows
+
+ if (mWindowDetachedWallpaper != detachedWallpaper) {
+ if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+ "Detached wallpaper changed from " + mWindowDetachedWallpaper
+ + " to " + detachedWallpaper);
+ mWindowDetachedWallpaper = detachedWallpaper;
+ mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
+ }
+
+ if (windowAnimationBackgroundColor != 0) {
+ // If the window that wants black is the current wallpaper
+ // target, then the black goes *below* the wallpaper so we
+ // don't cause the wallpaper to suddenly disappear.
+ int animLayer = windowAnimationBackground.mAnimLayer;
+ WindowState win = windowAnimationBackground.mWin;
+ if (mWallpaperTarget == win
+ || mLowerWallpaperTarget == win || mUpperWallpaperTarget == win) {
+ final int N = winAnimatorList.size();
+ for (int i = 0; i < N; i++) {
+ WindowStateAnimator winAnimator = winAnimatorList.get(i);
+ if (winAnimator.mIsWallpaper) {
+ animLayer = winAnimator.mAnimLayer;
+ break;
+ }
+ }
+ }
+
+ mWindowAnimationBackgroundSurface.show(mDw, mDh,
+ animLayer - WindowManagerService.LAYER_OFFSET_DIM,
+ windowAnimationBackgroundColor);
+ } else {
+ mWindowAnimationBackgroundSurface.hide();
+ }
+ }
+
private void testTokenMayBeDrawnLocked() {
// See if any windows have been drawn, so they (and others
// associated with them) can now be shown.
- final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens;
- final int NT = appTokens.size();
+ final int NT = mAppAnimators.size();
for (int i=0; i<NT; i++) {
- AppWindowToken wtoken = appTokens.get(i);
+ AppWindowAnimator appAnimator = mAppAnimators.get(i);
+ AppWindowToken wtoken = appAnimator.mAppToken;
final boolean allDrawn = wtoken.allDrawn;
- if (allDrawn != wtoken.mAppAnimator.allDrawn) {
- wtoken.mAppAnimator.allDrawn = allDrawn;
+ if (allDrawn != appAnimator.allDrawn) {
+ appAnimator.allDrawn = allDrawn;
if (allDrawn) {
// The token has now changed state to having all
// windows shown... what to do, what to do?
- if (wtoken.mAppAnimator.freezingScreen) {
- wtoken.mAppAnimator.showAllWindowsLocked();
+ if (appAnimator.freezingScreen) {
+ appAnimator.showAllWindowsLocked();
mService.unsetAppFreezingScreenLocked(wtoken, false, true);
if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
"Setting mOrientationChangeComplete=true because wtoken "
@@ -419,7 +546,7 @@ public class WindowAnimator {
// We can now show all of the drawn windows!
if (!mService.mOpeningApps.contains(wtoken)) {
- mAnimating |= wtoken.mAppAnimator.showAllWindowsLocked();
+ mAnimating |= appAnimator.showAllWindowsLocked();
}
}
}
@@ -427,13 +554,10 @@ public class WindowAnimator {
}
}
- private void performAnimationsLocked() {
- mForceHiding = KEYGUARD_NOT_SHOWN;
- mDetachedWallpaper = null;
- mWindowAnimationBackground = null;
- mWindowAnimationBackgroundColor = 0;
+ private void performAnimationsLocked(final WinAnimatorList winAnimatorList) {
+ updateWindowsLocked(winAnimatorList);
+ updateWallpaperLocked(winAnimatorList);
- updateWindowsAndWallpaperLocked();
if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
mPendingActions |= WALLPAPER_ACTION_PENDING;
}
@@ -441,10 +565,21 @@ public class WindowAnimator {
testTokenMayBeDrawnLocked();
}
- synchronized void animate() {
+ // TODO(cmautner): Change the following comment when no longer locked on mWindowMap */
+ /** Locked on mService.mWindowMap and this. */
+ private void animateLocked() {
+ if (!mInitialized) {
+ return;
+ }
+ for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) {
+ animateLocked(mWinAnimatorLists.get(i));
+ }
+ }
+
+ private void animateLocked(final WinAnimatorList winAnimatorList) {
mPendingLayoutChanges = 0;
mCurrentTime = SystemClock.uptimeMillis();
- mBulkUpdateParams = 0;
+ mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
boolean wasAnimating = mAnimating;
mAnimating = false;
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
@@ -457,8 +592,7 @@ public class WindowAnimator {
try {
updateWindowsAppsAndRotationAnimationsLocked();
- performAnimationsLocked();
- testWallpaperAndBackgroundLocked();
+ performAnimationsLocked(winAnimatorList);
// THIRD LOOP: Update the surfaces of all windows.
@@ -466,9 +600,9 @@ public class WindowAnimator {
mScreenRotationAnimation.updateSurfaces();
}
- final int N = mWinAnimators.size();
+ final int N = winAnimatorList.size();
for (int i = 0; i < N; i++) {
- mWinAnimators.get(i).prepareSurfaceLocked(true);
+ winAnimatorList.get(i).prepareSurfaceLocked(true);
}
if (mDimParams != null) {
@@ -497,10 +631,14 @@ public class WindowAnimator {
Surface.closeTransaction();
}
- mService.bulkSetParameters(mBulkUpdateParams, mPendingLayoutChanges);
+ if (mBulkUpdateParams != 0 || mPendingLayoutChanges != 0) {
+ updateAnimToLayoutLocked();
+ }
if (mAnimating) {
- mService.scheduleAnimationLocked();
+ synchronized (mService.mLayoutToAnim) {
+ mService.scheduleAnimationLocked();
+ }
} else if (wasAnimating) {
mService.requestTraversalLocked();
}
@@ -524,28 +662,6 @@ public class WindowAnimator {
mInnerDh = appHeight;
}
- void startDimming(final WindowStateAnimator winAnimator, final float target,
- final int width, final int height) {
- if (mDimAnimator == null) {
- mDimAnimator = new DimAnimator(mService.mFxSession);
- }
- // Only set dim params on the highest dimmed layer.
- final WindowStateAnimator dimWinAnimator = mDimParams == null
- ? null : mDimParams.mDimWinAnimator;
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed one.
- if (winAnimator.mSurfaceShown &&
- (dimWinAnimator == null || !dimWinAnimator.mSurfaceShown
- || dimWinAnimator.mAnimLayer < winAnimator.mAnimLayer)) {
- mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
- new DimAnimator.Parameters(winAnimator, width, height, target)));
- }
- }
-
- // TODO(cmautner): Move into Handler
- void stopDimming() {
- mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS, null));
- }
-
boolean isDimming() {
return mDimParams != null;
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 7011343..42bc7ce 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import com.android.internal.app.IBatteryStats;
@@ -42,12 +43,13 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AttributeCache;
import com.android.server.EventLogTags;
-import com.android.server.PowerManagerService;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
-import com.android.server.input.InputFilter;
+import com.android.server.display.DisplayDeviceInfo;
+import com.android.server.display.DisplayManagerService;
import com.android.server.input.InputManagerService;
-import com.android.server.pm.ShutdownThread;
+import com.android.server.power.PowerManagerService;
+import com.android.server.power.ShutdownThread;
import android.Manifest;
import android.app.ActivityManagerNative;
@@ -55,6 +57,7 @@ import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
+import android.animation.ValueAnimator;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -69,15 +72,15 @@ import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
-import android.os.BatteryStats;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
-import android.os.LocalPowerManager;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -89,22 +92,25 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.TokenWatcher;
import android.os.Trace;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.FloatMath;
import android.util.Log;
-import android.util.LogPrinter;
+import android.util.SparseArray;
+//import android.util.LogPrinter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.view.Choreographer;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IApplicationToken;
+import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindow;
@@ -119,8 +125,9 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
-import android.view.WindowManagerImpl;
+import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerPolicy.FakeWindow;
@@ -131,6 +138,7 @@ import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
+import android.view.animation.Transformation;
import java.io.BufferedWriter;
import java.io.DataInputStream;
@@ -151,6 +159,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
@@ -267,58 +276,31 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
- private static final String SYSTEM_HEADLESS = "ro.config.headless";
- /**
- * Condition waited on by {@link #reenableKeyguard} to know the call to
- * the window policy has finished.
- * This is set to true only if mKeyguardTokenWatcher.acquired() has
- * actually disabled the keyguard.
- */
- private boolean mKeyguardDisabled = false;
+ final private KeyguardDisableHandler mKeyguardDisableHandler;
private final boolean mHeadless;
- private static final int ALLOW_DISABLE_YES = 1;
- private static final int ALLOW_DISABLE_NO = 0;
- private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager
- private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher
-
private static final float THUMBNAIL_ANIMATION_DECELERATE_FACTOR = 1.5f;
- final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher(
- new Handler(), "WindowManagerService.mKeyguardTokenWatcher") {
- @Override
- public void acquired() {
- if (shouldAllowDisableKeyguard()) {
- mPolicy.enableKeyguard(false);
- mKeyguardDisabled = true;
- } else {
- Log.v(TAG, "Not disabling keyguard since device policy is enforced");
- }
- }
- @Override
- public void released() {
- mPolicy.enableKeyguard(true);
- synchronized (mKeyguardTokenWatcher) {
- mKeyguardDisabled = false;
- mKeyguardTokenWatcher.notifyAll();
- }
- }
- };
-
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mPolicy.enableKeyguard(true);
- synchronized(mKeyguardTokenWatcher) {
- // lazily evaluate this next time we're asked to disable keyguard
- mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN;
- mKeyguardDisabled = false;
+ final String action = intent.getAction();
+ if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
+ mKeyguardDisableHandler.sendEmptyMessage(
+ KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED);
+ } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ Slog.v(TAG, "Switching user from " + mCurrentUserId + " to " + newUserId);
+ mCurrentUserId = newUserId;
}
}
};
+ // Current user when multi-user is enabled. Don't show windows of non-current user.
+ int mCurrentUserId;
+
final Context mContext;
final boolean mHaveInputMethods;
@@ -383,11 +365,6 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();
/**
- * Z-ordered (bottom-most first) list of all Window objects.
- */
- final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
-
- /**
* Fake windows added to the window manager. Note: ordered from top to
* bottom, opposite of mWindows.
*/
@@ -463,29 +440,8 @@ public class WindowManagerService extends IWindowManager.Stub
String mLastANRState;
- // This protects the following display size properties, so that
- // getDisplaySize() doesn't need to acquire the global lock. This is
- // needed because the window manager sometimes needs to use ActivityThread
- // while it has its global state locked (for example to load animation
- // resources), but the ActivityThread also needs get the current display
- // size sometimes when it has its package lock held.
- //
- // These will only be modified with both mWindowMap and mDisplaySizeLock
- // held (in that order) so the window manager doesn't need to acquire this
- // lock when needing these values in its normal operation.
- final Object mDisplaySizeLock = new Object();
- int mInitialDisplayWidth = 0;
- int mInitialDisplayHeight = 0;
- int mBaseDisplayWidth = 0;
- int mBaseDisplayHeight = 0;
- int mCurDisplayWidth = 0;
- int mCurDisplayHeight = 0;
- int mAppDisplayWidth = 0;
- int mAppDisplayHeight = 0;
- int mSmallestDisplayWidth = 0;
- int mSmallestDisplayHeight = 0;
- int mLargestDisplayWidth = 0;
- int mLargestDisplayHeight = 0;
+ /** All DisplayDontents in the world, kept here */
+ private SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>();
int mRotation = 0;
int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -496,6 +452,7 @@ public class WindowManagerService extends IWindowManager.Stub
final Rect mSystemDecorRect = new Rect();
int mSystemDecorLayer = 0;
+ final Rect mScreenRect = new Rect();
int mPendingLayoutChanges = 0;
boolean mLayoutNeeded = true;
@@ -512,9 +469,9 @@ public class WindowManagerService extends IWindowManager.Stub
// State while inside of layoutAndPlaceSurfacesLocked().
boolean mFocusMayChange;
-
+
Configuration mCurConfiguration = new Configuration();
-
+
// This is held as long as we have the screen frozen, to give us time to
// perform a rotation animation when turning off shows the lock screen which
// changes the orientation.
@@ -529,7 +486,8 @@ public class WindowManagerService extends IWindowManager.Stub
int mNextAppTransitionType = ActivityOptions.ANIM_NONE;
String mNextAppTransitionPackage;
Bitmap mNextAppTransitionThumbnail;
- boolean mNextAppTransitionDelayed;
+ // Used for thumbnail transitions. True if we're scaling up, false if scaling down
+ boolean mNextAppTransitionScaleUp;
IRemoteCallback mNextAppTransitionCallback;
int mNextAppTransitionEnter;
int mNextAppTransitionExit;
@@ -545,7 +503,7 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
- Display mDisplay;
+ Display mDefaultDisplay;
boolean mIsTouchDevice;
@@ -583,10 +541,10 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState mWallpaperTarget = null;
// If non-null, we are in the middle of animating from one wallpaper target
// to another, and this is the lower one in Z-order.
- WindowState mLowerWallpaperTarget = null;
+ private WindowState mLowerWallpaperTarget = null;
// If non-null, we are in the middle of animating from one wallpaper target
// to another, and this is the higher one in Z-order.
- WindowState mUpperWallpaperTarget = null;
+ private WindowState mUpperWallpaperTarget = null;
int mWallpaperAnimLayerAdjustment;
float mLastWallpaperX = -1;
float mLastWallpaperY = -1;
@@ -611,6 +569,8 @@ public class WindowManagerService extends IWindowManager.Stub
float mAnimatorDurationScale = 1.0f;
final InputManagerService mInputManager;
+ final DisplayManagerService mDisplayManagerService;
+ final DisplayManager mDisplayManager;
// Who is holding the screen on.
Session mHoldingScreenOn;
@@ -626,7 +586,7 @@ public class WindowManagerService extends IWindowManager.Stub
static final int SET_UPDATE_ROTATION = 1 << 0;
static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
- static final int CLEAR_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
+ static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
static final int SET_TURN_ON_SCREEN = 1 << 4;
boolean mWallpaperForceHidingChanged = false;
@@ -641,7 +601,45 @@ public class WindowManagerService extends IWindowManager.Stub
private float mButtonBrightness = -1;
private boolean mUpdateRotation = false;
}
- LayoutFields mInnerFields = new LayoutFields();
+ final LayoutFields mInnerFields = new LayoutFields();
+
+ static class AppWindowAnimParams {
+ AppWindowAnimator mAppAnimator;
+ ArrayList<WindowStateAnimator> mWinAnimators;
+
+ public AppWindowAnimParams(final AppWindowAnimator appAnimator) {
+ mAppAnimator = appAnimator;
+
+ final AppWindowToken wtoken = appAnimator.mAppToken;
+ mWinAnimators = new ArrayList<WindowStateAnimator>();
+ final int N = wtoken.allAppWindows.size();
+ for (int i = 0; i < N; i++) {
+ mWinAnimators.add(wtoken.allAppWindows.get(i).mWinAnimator);
+ }
+ }
+ }
+
+ static class LayoutToAnimatorParams {
+ boolean mParamsModified;
+
+ static final long WALLPAPER_TOKENS_CHANGED = 1 << 0;
+ long mChanges;
+
+ boolean mAnimationScheduled;
+ ArrayList<WinAnimatorList> mWinAnimatorLists = new ArrayList<WinAnimatorList>();
+ WindowState mWallpaperTarget;
+ WindowState mLowerWallpaperTarget;
+ WindowState mUpperWallpaperTarget;
+ DimAnimator.Parameters mDimParams;
+ ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
+ ArrayList<AppWindowAnimParams> mAppWindowAnimParams = new ArrayList<AppWindowAnimParams>();
+ }
+ /** Params from WindowManagerService to WindowAnimator. Do not modify or read without first
+ * locking on either mWindowMap or mAnimator and then on mLayoutToAnim */
+ final LayoutToAnimatorParams mLayoutToAnim = new LayoutToAnimatorParams();
+
+ /** The lowest wallpaper target with a detached wallpaper animation on it. */
+ WindowState mWindowDetachedWallpaper = null;
/** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
* is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
@@ -650,33 +648,6 @@ public class WindowManagerService extends IWindowManager.Stub
/** Only do a maximum of 6 repeated layouts. After that quit */
private int mLayoutRepeatCount;
- private final class AnimationRunnable implements Runnable {
- @Override
- public void run() {
- synchronized(mWindowMap) {
- mAnimationScheduled = false;
- // Update animations of all applications, including those
- // associated with exiting/removed apps
- synchronized (mAnimator) {
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmAnimate");
- final ArrayList<WindowStateAnimator> winAnimators = mAnimator.mWinAnimators;
- winAnimators.clear();
- final int N = mWindows.size();
- for (int i = 0; i < N; i++) {
- final WindowStateAnimator winAnimator = mWindows.get(i).mWinAnimator;
- if (winAnimator.mSurface != null) {
- winAnimators.add(winAnimator);
- }
- }
- mAnimator.animate();
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- }
- }
- }
- }
- final AnimationRunnable mAnimationRunnable = new AnimationRunnable();
- boolean mAnimationScheduled;
-
final WindowAnimator mAnimator;
final class DragInputEventReceiver extends InputEventReceiver {
@@ -769,9 +740,10 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean mOnlyCore;
public static WindowManagerService main(Context context,
- PowerManagerService pm, boolean haveInputMethods, boolean allowBootMsgs,
+ PowerManagerService pm, DisplayManagerService dm,
+ boolean haveInputMethods, boolean allowBootMsgs,
boolean onlyCore) {
- WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs, onlyCore);
+ WMThread thr = new WMThread(context, pm, dm, haveInputMethods, allowBootMsgs, onlyCore);
thr.start();
synchronized (thr) {
@@ -790,15 +762,18 @@ public class WindowManagerService extends IWindowManager.Stub
private final Context mContext;
private final PowerManagerService mPM;
+ private final DisplayManagerService mDisplayManager;
private final boolean mHaveInputMethods;
private final boolean mAllowBootMessages;
private final boolean mOnlyCore;
public WMThread(Context context, PowerManagerService pm,
+ DisplayManagerService dm,
boolean haveInputMethods, boolean allowBootMsgs, boolean onlyCore) {
super("WindowManager");
mContext = context;
mPM = pm;
+ mDisplayManager = dm;
mHaveInputMethods = haveInputMethods;
mAllowBootMessages = allowBootMsgs;
mOnlyCore = onlyCore;
@@ -809,7 +784,7 @@ public class WindowManagerService extends IWindowManager.Stub
Looper.prepare();
//Looper.myLooper().setMessageLogging(new LogPrinter(
// android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
- WindowManagerService s = new WindowManagerService(mContext, mPM,
+ WindowManagerService s = new WindowManagerService(mContext, mPM, mDisplayManager,
mHaveInputMethods, mAllowBootMessages, mOnlyCore);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
@@ -833,30 +808,30 @@ public class WindowManagerService extends IWindowManager.Stub
private final WindowManagerPolicy mPolicy;
private final WindowManagerService mService;
private final Context mContext;
- private final PowerManagerService mPM;
boolean mRunning = false;
public PolicyThread(WindowManagerPolicy policy,
- WindowManagerService service, Context context,
- PowerManagerService pm) {
+ WindowManagerService service, Context context) {
super("WindowManagerPolicy");
mPolicy = policy;
mService = service;
mContext = context;
- mPM = pm;
}
@Override
public void run() {
Looper.prepare();
WindowManagerPolicyThread.set(this, Looper.myLooper());
-
+
//Looper.myLooper().setMessageLogging(new LogPrinter(
// Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
- mPolicy.init(mContext, mService, mService, mPM);
+ mPolicy.init(mContext, mService, mService);
+ mService.mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
synchronized (this) {
mRunning = true;
@@ -873,6 +848,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private WindowManagerService(Context context, PowerManagerService pm,
+ DisplayManagerService displayManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -880,7 +856,11 @@ public class WindowManagerService extends IWindowManager.Stub
mOnlyCore = onlyCore;
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
- mHeadless = "1".equals(SystemProperties.get(SYSTEM_HEADLESS, "0"));
+ mDisplayManagerService = displayManager;
+ mDisplayManager = DisplayManager.getInstance();
+ mHeadless = displayManager.isHeadless();
+
+ mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
mPowerManager = pm;
mPowerManager.setPolicy(mPolicy);
@@ -897,22 +877,25 @@ public class WindowManagerService extends IWindowManager.Stub
Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
- mAnimatorDurationScale = Settings.System.getFloat(context.getContentResolver(),
- Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale);
+ setAnimatorDurationScale(Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale));
// Track changes to DevicePolicyManager state so we can enable/disable keyguard.
IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ // Track user switching.
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mBroadcastReceiver, filter);
mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
- | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG");
+ | PowerManager.ON_AFTER_RELEASE, TAG);
mHoldingScreenWakeLock.setReferenceCounted(false);
mInputManager = new InputManagerService(context, mInputMonitor);
- mAnimator = new WindowAnimator(this, context, mPolicy);
+ mFxSession = new SurfaceSession();
+ mAnimator = new WindowAnimator(this);
- PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
+ PolicyThread thr = new PolicyThread(mPolicy, this, context);
thr.start();
synchronized (thr) {
@@ -928,7 +911,6 @@ public class WindowManagerService extends IWindowManager.Stub
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
- mFxSession = new SurfaceSession();
Surface.openTransaction();
createWatermark();
@@ -955,34 +937,31 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void placeWindowAfter(WindowState pos, WindowState window) {
- final int i = mWindows.indexOf(pos);
+ final WindowList windows = pos.getWindowList();
+ final int i = windows.indexOf(pos);
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
- + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
- mWindows.add(i+1, window);
+ + (i+1) + " of " + windows.size() + " (after " + pos + ")");
+ windows.add(i+1, window);
mWindowsChanged = true;
}
private void placeWindowBefore(WindowState pos, WindowState window) {
- final int i = mWindows.indexOf(pos);
+ final WindowList windows = pos.getWindowList();
+ final int i = windows.indexOf(pos);
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
- + i + " of " + mWindows.size() + " (before " + pos + ")");
- mWindows.add(i, window);
+ + i + " of " + windows.size() + " (before " + pos + ")");
+ windows.add(i, window);
mWindowsChanged = true;
}
//This method finds out the index of a window that has the same app token as
//win. used for z ordering the windows in mWindows
private int findIdxBasedOnAppTokens(WindowState win) {
- //use a local variable to cache mWindows
- ArrayList<WindowState> localmWindows = mWindows;
- int jmax = localmWindows.size();
- if(jmax == 0) {
- return -1;
- }
- for(int j = (jmax-1); j >= 0; j--) {
- WindowState wentry = localmWindows.get(j);
+ WindowList windows = win.getWindowList();
+ for(int j = windows.size() - 1; j >= 0; j--) {
+ WindowState wentry = windows.get(j);
if(wentry.mAppToken == win.mAppToken) {
return j;
}
@@ -993,9 +972,9 @@ public class WindowManagerService extends IWindowManager.Stub
private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
- final ArrayList<WindowState> localmWindows = mWindows;
- final int N = localmWindows.size();
+ final WindowList windows = win.getWindowList();
+ final int N = windows.size();
final WindowState attached = win.mAttachedWindow;
int i;
if (attached == null) {
@@ -1027,7 +1006,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.v(TAG, "Adding window " + win + " at "
+ (newIdx+1) + " of " + N);
}
- localmWindows.add(newIdx+1, win);
+ windows.add(newIdx+1, win);
mWindowsChanged = true;
}
}
@@ -1100,7 +1079,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Just search for the start of this layer.
final int myLayer = win.mBaseLayer;
for (i=0; i<N; i++) {
- WindowState w = localmWindows.get(i);
+ WindowState w = windows.get(i);
if (w.mBaseLayer > myLayer) {
break;
}
@@ -1109,7 +1088,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.v(TAG, "Adding window " + win + " at "
+ i + " of " + N);
}
- localmWindows.add(i, win);
+ windows.add(i, win);
mWindowsChanged = true;
}
}
@@ -1118,7 +1097,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Figure out where window should go, based on layer.
final int myLayer = win.mBaseLayer;
for (i=N-1; i>=0; i--) {
- if (localmWindows.get(i).mBaseLayer <= myLayer) {
+ if (windows.get(i).mBaseLayer <= myLayer) {
i++;
break;
}
@@ -1127,9 +1106,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + win + " at "
+ i + " of " + N);
- localmWindows.add(i, win);
+ windows.add(i, win);
mWindowsChanged = true;
}
+
if (addToToken) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(tokenWindowsPos, win);
@@ -1157,8 +1137,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(i, win);
}
- placeWindowBefore(
- wSublayer >= 0 ? attached : w, win);
+ placeWindowBefore(wSublayer >= 0 ? attached : w, win);
break;
}
} else {
@@ -1226,13 +1205,16 @@ public class WindowManagerService extends IWindowManager.Stub
* @return The index+1 in mWindows of the discovered target.
*/
int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
- final ArrayList<WindowState> localmWindows = mWindows;
- final int N = localmWindows.size();
+ // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
+ // same display. Or even when the current IME/target are not on the same screen as the next
+ // IME/target. For now only look for input windows on the main screen.
+ WindowList windows = getDefaultWindowList();
+ final int N = windows.size();
WindowState w = null;
int i = N;
while (i > 0) {
i--;
- w = localmWindows.get(i);
+ w = windows.get(i);
if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG, "Checking window @" + i
+ " " + w + " fl=0x" + Integer.toHexString(w.mAttrs.flags));
@@ -1247,7 +1229,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (!willMove
&& w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& i > 0) {
- WindowState wb = localmWindows.get(i-1);
+ WindowState wb = windows.get(i-1);
if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
i--;
w = wb;
@@ -1267,12 +1249,13 @@ public class WindowManagerService extends IWindowManager.Stub
// the IME shown: when the Dialog is dismissed, we want to keep
// the IME above it until it is completely gone so it doesn't drop
// behind the dialog or its full-screen scrim.
- if (mInputMethodTarget != null && w != null
- && mInputMethodTarget.isDisplayedLw()
- && mInputMethodTarget.mExiting) {
- if (mInputMethodTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) {
- w = mInputMethodTarget;
- i = localmWindows.indexOf(w);
+ final WindowState curTarget = mInputMethodTarget;
+ if (curTarget != null && w != null
+ && curTarget.isDisplayedLw()
+ && curTarget.mExiting) {
+ if (curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) {
+ w = curTarget;
+ i = windows.indexOf(w);
if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, switching to: " + w);
}
}
@@ -1281,20 +1264,20 @@ public class WindowManagerService extends IWindowManager.Stub
+ w + " willMove=" + willMove);
if (willMove && w != null) {
- final WindowState curTarget = mInputMethodTarget;
- if (curTarget != null && curTarget.mAppToken != null) {
+ AppWindowToken token = curTarget == null ? null : curTarget.mAppToken;
+ if (token != null) {
// Now some fun for dealing with window animations that
// modify the Z order. We need to look at all windows below
// the current target that are in this app, finding the highest
// visible one in layering.
- AppWindowToken token = curTarget.mAppToken;
WindowState highestTarget = null;
int highestPos = 0;
if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
- int pos = localmWindows.indexOf(curTarget);
+ WindowList curWindows = curTarget.getWindowList();
+ int pos = curWindows.indexOf(curTarget);
while (pos >= 0) {
- WindowState win = localmWindows.get(pos);
+ WindowState win = curWindows.get(pos);
if (win.mAppToken != token) {
break;
}
@@ -1339,15 +1322,8 @@ public class WindowManagerService extends IWindowManager.Stub
//Slog.i(TAG, "Placing input method @" + (i+1));
if (w != null) {
if (willMove) {
- if (DEBUG_INPUT_METHOD) {
- RuntimeException e = null;
- if (!HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- Slog.w(TAG, "Moving IM target from "
- + mInputMethodTarget + " to " + w, e);
- }
+ if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to "
+ + w + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4)));
mInputMethodTarget = w;
mInputMethodTargetWaitingAnim = false;
if (w.mAppToken != null) {
@@ -1359,15 +1335,8 @@ public class WindowManagerService extends IWindowManager.Stub
return i+1;
}
if (willMove) {
- if (DEBUG_INPUT_METHOD) {
- RuntimeException e = null;
- if (!HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- Slog.w(TAG, "Moving IM target from "
- + mInputMethodTarget + " to null", e);
- }
+ if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to null."
+ + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4)));
mInputMethodTarget = null;
setInputMethodAnimLayerAdjustment(0);
}
@@ -1380,7 +1349,8 @@ public class WindowManagerService extends IWindowManager.Stub
win.mTargetAppToken = mInputMethodTarget.mAppToken;
if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding input method window " + win + " at " + pos);
- mWindows.add(pos, win);
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ getDefaultWindowList().add(pos, win);
mWindowsChanged = true;
moveInputMethodDialogsLocked(pos+1);
return;
@@ -1418,22 +1388,23 @@ public class WindowManagerService extends IWindowManager.Stub
}
private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
- int wpos = mWindows.indexOf(win);
+ WindowList windows = win.getWindowList();
+ int wpos = windows.indexOf(win);
if (wpos >= 0) {
if (wpos < interestingPos) interestingPos--;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win);
- mWindows.remove(wpos);
+ windows.remove(wpos);
mWindowsChanged = true;
int NC = win.mChildWindows.size();
while (NC > 0) {
NC--;
WindowState cw = win.mChildWindows.get(NC);
- int cpos = mWindows.indexOf(cw);
+ int cpos = windows.indexOf(cw);
if (cpos >= 0) {
if (cpos < interestingPos) interestingPos--;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing child at "
+ cpos + ": " + cw);
- mWindows.remove(cpos);
+ windows.remove(cpos);
}
}
}
@@ -1445,27 +1416,29 @@ public class WindowManagerService extends IWindowManager.Stub
// This is a hack to get all of the child windows added as well
// at the right position. Child windows should be rare and
// this case should be rare, so it shouldn't be that big a deal.
- int wpos = mWindows.indexOf(win);
+ WindowList windows = win.getWindowList();
+ int wpos = windows.indexOf(win);
if (wpos >= 0) {
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos
- + ": " + win);
- mWindows.remove(wpos);
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos + ": " + win);
+ windows.remove(wpos);
mWindowsChanged = true;
reAddWindowLocked(wpos, win);
}
}
- void logWindowList(String prefix) {
- int N = mWindows.size();
+ void logWindowList(final WindowList windows, String prefix) {
+ int N = windows.size();
while (N > 0) {
N--;
- Slog.v(TAG, prefix + "#" + N + ": " + mWindows.get(N));
+ Slog.v(TAG, prefix + "#" + N + ": " + windows.get(N));
}
}
void moveInputMethodDialogsLocked(int pos) {
ArrayList<WindowState> dialogs = mInputMethodDialogs;
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ WindowList windows = getDefaultWindowList();
final int N = dialogs.size();
if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Removing " + N + " dialogs w/pos=" + pos);
for (int i=0; i<N; i++) {
@@ -1473,13 +1446,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "Window list w/pos=" + pos);
- logWindowList(" ");
+ logWindowList(windows, " ");
}
if (pos >= 0) {
final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
- if (pos < mWindows.size()) {
- WindowState wp = mWindows.get(pos);
+ if (pos < windows.size()) {
+ WindowState wp = windows.get(pos);
if (wp == mInputMethodWindow) {
pos++;
}
@@ -1492,7 +1465,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "Final window list:");
- logWindowList(" ");
+ logWindowList(windows, " ");
}
return;
}
@@ -1502,7 +1475,7 @@ public class WindowManagerService extends IWindowManager.Stub
reAddWindowToListInOrderLocked(win);
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "No IM target, final list:");
- logWindowList(" ");
+ logWindowList(windows, " ");
}
}
}
@@ -1514,6 +1487,9 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ WindowList windows = getDefaultWindowList();
+
int imPos = findDesiredInputMethodWindowIndexLocked(true);
if (imPos >= 0) {
// In this case, the input method windows are to be placed
@@ -1521,9 +1497,9 @@ public class WindowManagerService extends IWindowManager.Stub
// First check to see if the input method windows are already
// located here, and contiguous.
- final int N = mWindows.size();
+ final int N = windows.size();
WindowState firstImWin = imPos < N
- ? mWindows.get(imPos) : null;
+ ? windows.get(imPos) : null;
// Figure out the actual input method window that should be
// at the bottom of their stack.
@@ -1539,7 +1515,7 @@ public class WindowManagerService extends IWindowManager.Stub
// First find the top IM window.
int pos = imPos+1;
while (pos < N) {
- if (!(mWindows.get(pos)).mIsImWindow) {
+ if (!(windows.get(pos)).mIsImWindow) {
break;
}
pos++;
@@ -1547,7 +1523,7 @@ public class WindowManagerService extends IWindowManager.Stub
pos++;
// Now there should be no more input method windows above.
while (pos < N) {
- if ((mWindows.get(pos)).mIsImWindow) {
+ if ((windows.get(pos)).mIsImWindow) {
break;
}
pos++;
@@ -1561,18 +1537,18 @@ public class WindowManagerService extends IWindowManager.Stub
if (imWin != null) {
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "Moving IM from " + imPos);
- logWindowList(" ");
+ logWindowList(windows, " ");
}
imPos = tmpRemoveWindowLocked(imPos, imWin);
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "List after removing with new pos " + imPos + ":");
- logWindowList(" ");
+ logWindowList(windows, " ");
}
imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
reAddWindowLocked(imPos, imWin);
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "List after moving IM to " + imPos + ":");
- logWindowList(" ");
+ logWindowList(windows, " ");
}
if (DN > 0) moveInputMethodDialogsLocked(imPos+1);
} else {
@@ -1590,17 +1566,17 @@ public class WindowManagerService extends IWindowManager.Stub
reAddWindowToListInOrderLocked(imWin);
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG, "List with no IM target:");
- logWindowList(" ");
+ logWindowList(windows, " ");
}
- if (DN > 0) moveInputMethodDialogsLocked(-1);;
+ if (DN > 0) moveInputMethodDialogsLocked(-1);
} else {
- moveInputMethodDialogsLocked(-1);;
+ moveInputMethodDialogsLocked(-1);
}
}
if (needAssignLayers) {
- assignLayersLocked();
+ assignLayersLocked(windows);
}
return true;
@@ -1631,13 +1607,15 @@ public class WindowManagerService extends IWindowManager.Stub
mInnerFields.mWallpaperMayChange = false;
int changed = 0;
- final int dw = mAppDisplayWidth;
- final int dh = mAppDisplayHeight;
+ // TODO(multidisplay): Wallpapers on main screen only.
+ final DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo();
+ final int dw = displayInfo.appWidth;
+ final int dh = displayInfo.appHeight;
// First find top-most window that has asked to be on top of the
// wallpaper; all wallpapers go behind it.
- final ArrayList<WindowState> localmWindows = mWindows;
- int N = localmWindows.size();
+ final WindowList windows = getDefaultWindowList();
+ int N = windows.size();
WindowState w = null;
WindowState foundW = null;
int foundI = 0;
@@ -1647,7 +1625,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = N;
while (i > 0) {
i--;
- w = localmWindows.get(i);
+ w = windows.get(i);
if ((w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER)) {
if (topCurW == null) {
topCurW = w;
@@ -1656,7 +1634,7 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
topCurW = null;
- if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
+ if (w != mWindowDetachedWallpaper && w.mAppToken != null) {
// If this window's app token is hidden and not animating,
// it is of no interest to us.
if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
@@ -1682,7 +1660,7 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
break;
- } else if (w == mAnimator.mWindowDetachedWallpaper) {
+ } else if (w == mWindowDetachedWallpaper) {
windowDetachedI = i;
}
}
@@ -1741,7 +1719,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " old animation: " + oldAnim);
}
if (foundAnim && oldAnim) {
- int oldI = localmWindows.indexOf(oldW);
+ int oldI = windows.indexOf(oldW);
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "New i: " + foundI + " old i: " + oldI);
}
@@ -1760,7 +1738,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWallpaperTarget = oldW;
foundW = oldW;
foundI = oldI;
- }
+ }
// Now set the upper and lower wallpaper targets
// correctly, and make sure that we are positioning
// the wallpaper below the lower.
@@ -1825,7 +1803,7 @@ public class WindowManagerService extends IWindowManager.Stub
// AND any starting window associated with it, AND below the
// maximum layer the policy allows for wallpapers.
while (foundI > 0) {
- WindowState wb = localmWindows.get(foundI-1);
+ WindowState wb = windows.get(foundI-1);
if (wb.mBaseLayer < maxLayer &&
wb.mAttachedWindow != foundW &&
(foundW.mAttachedWindow == null ||
@@ -1851,7 +1829,7 @@ public class WindowManagerService extends IWindowManager.Stub
} else {
// Okay i is the position immediately above the wallpaper. Look at
// what is below it for later.
- foundW = foundI > 0 ? localmWindows.get(foundI-1) : null;
+ foundW = foundI > 0 ? windows.get(foundI-1) : null;
}
if (visible) {
@@ -1901,18 +1879,18 @@ public class WindowManagerService extends IWindowManager.Stub
if (wallpaper == foundW) {
foundI--;
foundW = foundI > 0
- ? localmWindows.get(foundI-1) : null;
+ ? windows.get(foundI-1) : null;
continue;
}
// The window didn't match... the current wallpaper window,
// wherever it is, is in the wrong place, so make sure it is
// not in the list.
- int oldIndex = localmWindows.indexOf(wallpaper);
+ int oldIndex = windows.indexOf(wallpaper);
if (oldIndex >= 0) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at "
+ oldIndex + ": " + wallpaper);
- localmWindows.remove(oldIndex);
+ windows.remove(oldIndex);
mWindowsChanged = true;
if (oldIndex < foundI) {
foundI--;
@@ -1925,7 +1903,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " from " + oldIndex + " to " + foundI);
}
- localmWindows.add(foundI, wallpaper);
+ windows.add(foundI, wallpaper);
mWindowsChanged = true;
changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
}
@@ -2040,14 +2018,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // TODO(cmautner): Move to WindowAnimator.
- void setWallpaperOffset(final WindowStateAnimator winAnimator, final int left, final int top) {
- mH.sendMessage(mH.obtainMessage(H.SET_WALLPAPER_OFFSET, left, top, winAnimator));
- }
-
void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
- final int dw = mAppDisplayWidth;
- final int dh = mAppDisplayHeight;
+ final DisplayContent displayContent = changingTarget.mDisplayContent;
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int dw = displayInfo.appWidth;
+ final int dh = displayInfo.appHeight;
WindowState target = mWallpaperTarget;
if (target != null) {
@@ -2079,19 +2054,8 @@ public class WindowManagerService extends IWindowManager.Stub
// TODO(cmautner): Don't move this from here, just lock the WindowAnimator.
if (winAnimator.mSurfaceX != wallpaper.mShownFrame.left
|| winAnimator.mSurfaceY != wallpaper.mShownFrame.top) {
- Surface.openTransaction();
- try {
- if (SHOW_TRANSACTIONS) logSurface(wallpaper,
- "POS " + wallpaper.mShownFrame.left
- + ", " + wallpaper.mShownFrame.top, null);
- setWallpaperOffset(winAnimator, (int) wallpaper.mShownFrame.left,
+ winAnimator.setWallpaperOffset((int) wallpaper.mShownFrame.left,
(int) wallpaper.mShownFrame.top);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error positioning surface of " + wallpaper
- + " pos=(" + wallpaper.mShownFrame.left
- + "," + wallpaper.mShownFrame.top + ")", e);
- }
- Surface.closeTransaction();
}
// We only want to be synchronous with one wallpaper.
sync = false;
@@ -2120,8 +2084,10 @@ public class WindowManagerService extends IWindowManager.Stub
void updateWallpaperVisibilityLocked() {
final boolean visible = isWallpaperVisible(mWallpaperTarget);
- final int dw = mAppDisplayWidth;
- final int dh = mAppDisplayHeight;
+ final DisplayContent displayContent = mWallpaperTarget.mDisplayContent;
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int dw = displayInfo.appWidth;
+ final int dh = displayInfo.appHeight;
int curTokenIndex = mWallpaperTokens.size();
while (curTokenIndex > 0) {
@@ -2146,12 +2112,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
-
+
public int addWindow(Session session, IWindow client, int seq,
- WindowManager.LayoutParams attrs, int viewVisibility,
+ WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
int res = mPolicy.checkAddPermission(attrs);
- if (res != WindowManagerImpl.ADD_OKAY) {
+ if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
@@ -2161,13 +2127,13 @@ public class WindowManagerService extends IWindowManager.Stub
long origId;
synchronized(mWindowMap) {
- if (mDisplay == null) {
+ if (mDefaultDisplay == null) {
throw new IllegalStateException("Display has not been initialialized");
}
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG, "Window " + client + " is already added");
- return WindowManagerImpl.ADD_DUPLICATE_ADD;
+ return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {
@@ -2175,13 +2141,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (attachedWindow == null) {
Slog.w(TAG, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
@@ -2192,22 +2158,22 @@ public class WindowManagerService extends IWindowManager.Stub
&& attrs.type <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (attrs.type == TYPE_INPUT_METHOD) {
Slog.w(TAG, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (attrs.type == TYPE_WALLPAPER) {
Slog.w(TAG, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (attrs.type == TYPE_DREAM) {
Slog.w(TAG, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
@@ -2217,68 +2183,69 @@ public class WindowManagerService extends IWindowManager.Stub
if (atoken == null) {
Slog.w(TAG, "Attempted to add window with non-application token "
+ token + ". Aborting.");
- return WindowManagerImpl.ADD_NOT_APP_TOKEN;
+ return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
- return WindowManagerImpl.ADD_APP_EXITING;
+ return WindowManagerGlobal.ADD_APP_EXITING;
}
if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
// No need for this guy!
if (localLOGV) Slog.v(
TAG, "**** NO NEED TO START: " + attrs.getTitle());
- return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;
+ return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
}
} else if (attrs.type == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (attrs.type == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
Slog.w(TAG, "Attempted to add wallpaper window with bad token "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (attrs.type == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
Slog.w(TAG, "Attempted to add Dream window with bad token "
+ attrs.token + ". Aborting.");
- return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
+ final DisplayContent displayContent = getDisplayContent(displayId);
win = new WindowState(this, session, client, token,
- attachedWindow, seq, attrs, viewVisibility);
+ attachedWindow, seq, attrs, viewVisibility, displayContent);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
- return WindowManagerImpl.ADD_APP_EXITING;
+ return WindowManagerGlobal.ADD_APP_EXITING;
}
mPolicy.adjustWindowParamsLw(win.mAttrs);
res = mPolicy.prepareAddWindowLw(win, attrs);
- if (res != WindowManagerImpl.ADD_OKAY) {
+ if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
-
+
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
-
+
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
// From now on, no exceptions or errors allowed!
- res = WindowManagerImpl.ADD_OKAY;
+ res = WindowManagerGlobal.ADD_OKAY;
origId = Binder.clearCallingIdentity();
@@ -2322,10 +2289,10 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.getContentInsetHintLw(attrs, outContentInsets);
if (mInTouchMode) {
- res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE;
+ res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
if (win.mAppToken == null || !win.mAppToken.clientHidden) {
- res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE;
+ res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
mInputMonitor.setUpdateInputWindowsNeededLw();
@@ -2343,7 +2310,7 @@ public class WindowManagerService extends IWindowManager.Stub
moveInputMethodWindowsIfNeededLocked(false);
}
- assignLayersLocked();
+ assignLayersLocked(displayContent.getWindowList());
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
@@ -2357,7 +2324,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (localLOGV) Slog.v(
TAG, "New client " + client.asBinder()
+ ": window=" + win);
-
+
if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
reportNewConfig = true;
}
@@ -2485,7 +2452,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win);
mWindowMap.remove(win.mClient.asBinder());
- mWindows.remove(win);
+
+ final WindowList windows = win.getWindowList();
+ windows.remove(win);
mPendingRemove.remove(win);
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
@@ -2543,14 +2512,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (!mInLayout) {
- assignLayersLocked();
+ assignLayersLocked(windows);
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
}
-
+
mInputMonitor.updateInputWindowsLw(true /*force*/);
}
@@ -2683,6 +2652,32 @@ public class WindowManagerService extends IWindowManager.Stub
return null;
}
+ public void setUniverseTransformLocked(WindowState window, float alpha,
+ float offx, float offy, float dsdx, float dtdx, float dsdy, float dtdy) {
+ Transformation transform = window.mWinAnimator.mUniverseTransform;
+ transform.setAlpha(alpha);
+ Matrix matrix = transform.getMatrix();
+ matrix.getValues(mTmpFloats);
+ mTmpFloats[Matrix.MTRANS_X] = offx;
+ mTmpFloats[Matrix.MTRANS_Y] = offy;
+ mTmpFloats[Matrix.MSCALE_X] = dsdx;
+ mTmpFloats[Matrix.MSKEW_Y] = dtdx;
+ mTmpFloats[Matrix.MSKEW_X] = dsdy;
+ mTmpFloats[Matrix.MSCALE_Y] = dtdy;
+ matrix.setValues(mTmpFloats);
+ final DisplayInfo displayInfo = window.mDisplayContent.getDisplayInfo();
+ final RectF dispRect = new RectF(0, 0,
+ displayInfo.logicalWidth, displayInfo.logicalHeight);
+ matrix.mapRect(dispRect);
+ window.mGivenTouchableRegion.set(0, 0,
+ displayInfo.logicalWidth, displayInfo.logicalHeight);
+ window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top,
+ (int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE);
+ window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
@@ -2729,7 +2724,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
winAnimator.mSurfaceDestroyDeferred =
- (flags&WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
+ (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
int attrChanges = 0;
int flagChanges = 0;
@@ -2848,7 +2843,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
} catch (Exception e) {
mInputMonitor.updateInputWindowsLw(true /*force*/);
-
+
Slog.w(TAG, "Exception thrown when creating surface for client "
+ client + " (" + win.mAttrs.getTitle() + ")",
e);
@@ -2948,14 +2943,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
mLayoutNeeded = true;
- win.mGivenInsetsPending = (flags&WindowManagerImpl.RELAYOUT_INSETS_PENDING) != 0;
+ win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
if (assignLayers) {
- assignLayersLocked();
+ assignLayersLocked(win.getWindowList());
}
configChanged = updateOrientationFromAppTokensLocked(false);
performLayoutAndPlaceSurfacesLocked();
if (toBeDisplayed && win.mIsWallpaper) {
- updateWallpaperOffsetLocked(win, mAppDisplayWidth, mAppDisplayHeight, false);
+ DisplayInfo displayInfo = getDefaultDisplayInfo();
+ updateWallpaperOffsetLocked(win,
+ displayInfo.appWidth, displayInfo.appHeight, false);
}
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -2989,10 +2986,10 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
- return (inTouchMode ? WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE : 0)
- | (toBeDisplayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0)
- | (surfaceChanged ? WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED : 0)
- | (animating ? WindowManagerImpl.RELAYOUT_RES_ANIMATING : 0);
+ return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
+ | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
+ | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0)
+ | (animating ? WindowManagerGlobal.RELAYOUT_RES_ANIMATING : 0);
}
public void performDeferredDestroyWindow(Session session, IWindow client) {
@@ -3042,7 +3039,12 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
}
+ @Override
public float getWindowCompatibilityScale(IBinder windowToken) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "getWindowCompatibilityScale()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+ }
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(windowToken);
return (windowState != null) ? windowState.mGlobalScale : 1.0f;
@@ -3128,12 +3130,11 @@ public class WindowManagerService extends IWindowManager.Stub
a.setDetachWallpaper(true);
a.setDuration(duration);
return a;
- } else {
- // For normal animations, the exiting element just holds in place.
- Animation a = new AlphaAnimation(1, 1);
- a.setDuration(duration);
- return a;
}
+ // For normal animations, the exiting element just holds in place.
+ Animation a = new AlphaAnimation(1, 1);
+ a.setDuration(duration);
+ return a;
}
/**
@@ -3170,10 +3171,12 @@ public class WindowManagerService extends IWindowManager.Stub
duration = 300;
break;
}
+ // TODO(multidisplay): For now assume all app animation is on main display.
+ final DisplayInfo displayInfo = getDefaultDisplayInfo();
if (enter) {
// Entering app zooms out from the center of the initial rect.
- float scaleW = mNextAppTransitionStartWidth / (float) mAppDisplayWidth;
- float scaleH = mNextAppTransitionStartHeight / (float) mAppDisplayHeight;
+ float scaleW = mNextAppTransitionStartWidth / (float) displayInfo.appWidth;
+ float scaleH = mNextAppTransitionStartHeight / (float) displayInfo.appHeight;
Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
computePivot(mNextAppTransitionStartX, scaleW),
computePivot(mNextAppTransitionStartY, scaleH));
@@ -3193,13 +3196,13 @@ public class WindowManagerService extends IWindowManager.Stub
final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
com.android.internal.R.interpolator.decelerate_cubic);
a.setInterpolator(interpolator);
- a.initialize(mAppDisplayWidth, mAppDisplayHeight,
- mAppDisplayWidth, mAppDisplayHeight);
+ a.initialize(displayInfo.appWidth, displayInfo.appHeight,
+ displayInfo.appWidth, displayInfo.appHeight);
return a;
}
private Animation createThumbnailAnimationLocked(int transit,
- boolean enter, boolean thumb, boolean delayed) {
+ boolean enter, boolean thumb, boolean scaleUp) {
Animation a;
final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -3209,7 +3212,6 @@ public class WindowManagerService extends IWindowManager.Stub
// it is the standard duration for that. Otherwise we use the longer
// task transition duration.
int duration;
- int delayDuration = delayed ? 270 : 0;
switch (transit) {
case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
@@ -3217,62 +3219,102 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.integer.config_shortAnimTime);
break;
default:
- duration = delayed ? 250 : 300;
+ duration = 250;
break;
}
+ // TOOD(multidisplay): For now assume all app animation is on the main screen.
+ DisplayInfo displayInfo = getDefaultDisplayInfo();
if (thumb) {
// Animation for zooming thumbnail from its initial size to
// filling the screen.
- float scaleW = mAppDisplayWidth/thumbWidth;
- float scaleH = mAppDisplayHeight/thumbHeight;
-
- Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mNextAppTransitionStartX, 1/scaleW),
- computePivot(mNextAppTransitionStartY, 1/scaleH));
- AnimationSet set = new AnimationSet(true);
- Animation alpha = new AlphaAnimation(1, 0);
- scale.setDuration(duration);
- scale.setInterpolator(
- new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR));
- set.addAnimation(scale);
- alpha.setDuration(duration);
- set.addAnimation(alpha);
- set.setFillBefore(true);
- if (delayDuration > 0) {
- set.setStartOffset(delayDuration);
+ if (scaleUp) {
+ float scaleW = displayInfo.appWidth / thumbWidth;
+ float scaleH = displayInfo.appHeight / thumbHeight;
+
+ Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+ computePivot(mNextAppTransitionStartX, 1 / scaleW),
+ computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ AnimationSet set = new AnimationSet(true);
+ Animation alpha = new AlphaAnimation(1, 0);
+ scale.setDuration(duration);
+ scale.setInterpolator(
+ new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR));
+ set.addAnimation(scale);
+ alpha.setDuration(duration);
+ set.addAnimation(alpha);
+ set.setFillBefore(true);
+ a = set;
+ } else {
+ float scaleW = displayInfo.appWidth / thumbWidth;
+ float scaleH = displayInfo.appHeight / thumbHeight;
+
+ Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
+ computePivot(mNextAppTransitionStartX, 1 / scaleW),
+ computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ AnimationSet set = new AnimationSet(true);
+ Animation alpha = new AlphaAnimation(1, 1);
+ scale.setDuration(duration);
+ scale.setInterpolator(
+ new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR));
+ set.addAnimation(scale);
+ alpha.setDuration(duration);
+ set.addAnimation(alpha);
+ set.setFillBefore(true);
+
+ a = set;
}
- a = set;
} else if (enter) {
// Entering app zooms out from the center of the thumbnail.
- float scaleW = thumbWidth / mAppDisplayWidth;
- float scaleH = thumbHeight / mAppDisplayHeight;
- Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mNextAppTransitionStartX, scaleW),
- computePivot(mNextAppTransitionStartY, scaleH));
- scale.setDuration(duration);
- scale.setInterpolator(
- new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR));
- scale.setFillBefore(true);
- if (delayDuration > 0) {
- scale.setStartOffset(delayDuration);
+ if (scaleUp) {
+ float scaleW = thumbWidth / displayInfo.appWidth;
+ float scaleH = thumbHeight / displayInfo.appHeight;
+ Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
+ computePivot(mNextAppTransitionStartX, scaleW),
+ computePivot(mNextAppTransitionStartY, scaleH));
+ scale.setDuration(duration);
+ scale.setInterpolator(
+ new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR));
+ scale.setFillBefore(true);
+ a = scale;
+ } else {
+ // noop animation
+ a = new AlphaAnimation(1, 1);
+ a.setDuration(duration);
}
- a = scale;
} else {
- if (delayed) {
- a = new AlphaAnimation(1, 0);
- a.setStartOffset(0);
- a.setDuration(delayDuration - 120);
- a.setBackgroundColor(0xFF000000);
+ // Exiting app
+ if (scaleUp) {
+ // noop animation
+ a = new AlphaAnimation(1, 1);
+ a.setDuration(duration);
} else {
- a = createExitAnimationLocked(transit, duration);
+ float scaleW = thumbWidth / displayInfo.appWidth;
+ float scaleH = thumbHeight / displayInfo.appHeight;
+ Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+ computePivot(mNextAppTransitionStartX, scaleW),
+ computePivot(mNextAppTransitionStartY, scaleH));
+ scale.setDuration(duration);
+ scale.setInterpolator(
+ new DecelerateInterpolator(THUMBNAIL_ANIMATION_DECELERATE_FACTOR));
+ scale.setFillBefore(true);
+ AnimationSet set = new AnimationSet(true);
+ Animation alpha = new AlphaAnimation(1, 0);
+ set.addAnimation(scale);
+ alpha.setDuration(duration);
+ alpha.setInterpolator(new DecelerateInterpolator(
+ THUMBNAIL_ANIMATION_DECELERATE_FACTOR));
+ set.addAnimation(alpha);
+ set.setFillBefore(true);
+ set.setZAdjustment(Animation.ZORDER_TOP);
+ a = set;
}
}
a.setFillAfter(true);
final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
com.android.internal.R.interpolator.decelerate_quad);
a.setInterpolator(interpolator);
- a.initialize(mAppDisplayWidth, mAppDisplayHeight,
- mAppDisplayWidth, mAppDisplayHeight);
+ a.initialize(displayInfo.appWidth, displayInfo.appHeight,
+ displayInfo.appWidth, displayInfo.appHeight);
return a;
}
@@ -3299,14 +3341,14 @@ public class WindowManagerService extends IWindowManager.Stub
"applyAnimation: wtoken=" + wtoken
+ " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
+ " transit=" + transit + " Callers " + Debug.getCallers(3));
- } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL ||
- mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED) {
- boolean delayed = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED);
- a = createThumbnailAnimationLocked(transit, enter, false, delayed);
+ } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP ||
+ mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN) {
+ boolean scaleUp = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP);
+ a = createThumbnailAnimationLocked(transit, enter, false, scaleUp);
initialized = true;
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
- String animName = delayed ? "ANIM_THUMBNAIL_DELAYED" : "ANIM_THUMBNAIL";
+ String animName = scaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
+ " anim=" + a + " nextAppTransition=" + animName
+ " transit=" + transit + " Callers " + Debug.getCallers(3));
@@ -3440,7 +3482,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, msg);
return false;
}
-
+
boolean okToDisplay() {
return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOnFully();
}
@@ -3470,10 +3512,12 @@ public class WindowManagerService extends IWindowManager.Stub
mTokenMap.put(token, wtoken);
if (type == TYPE_WALLPAPER) {
mWallpaperTokens.add(wtoken);
+ updateLayoutToAnimWallpaperTokens();
}
}
}
+ @Override
public void removeWindowToken(IBinder token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"removeWindowToken()")) {
@@ -3499,7 +3543,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (win.isVisibleNow()) {
- win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false);
+ win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
+ false);
changed = true;
}
}
@@ -3515,6 +3560,7 @@ public class WindowManagerService extends IWindowManager.Stub
mExitingTokens.add(wtoken);
} else if (wtoken.windowType == TYPE_WALLPAPER) {
mWallpaperTokens.remove(wtoken);
+ updateLayoutToAnimWallpaperTokens();
}
}
@@ -3591,6 +3637,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void setAppGroupId(IBinder token, int groupId) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppGroupId()")) {
@@ -3617,9 +3664,11 @@ public class WindowManagerService extends IWindowManager.Stub
return mLastWindowForcedOrientation;
}
- int pos = mWindows.size() - 1;
+ // TODO(multidisplay): Change to the correct display.
+ final WindowList windows = getDefaultWindowList();
+ int pos = windows.size() - 1;
while (pos >= 0) {
- WindowState wtoken = mWindows.get(pos);
+ WindowState wtoken = windows.get(pos);
pos--;
if (wtoken.mAppToken != null) {
// We hit an application window. so the orientation will be determined by the
@@ -3633,9 +3682,9 @@ public class WindowManagerService extends IWindowManager.Stub
if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
(req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
continue;
- } else {
- return (mLastWindowForcedOrientation=req);
}
+
+ return (mLastWindowForcedOrientation=req);
}
return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
@@ -3685,7 +3734,7 @@ public class WindowManagerService extends IWindowManager.Stub
haveGroup = true;
curGroup = wtoken.groupId;
lastOrientation = wtoken.requestedOrientation;
- }
+ }
int or = wtoken.requestedOrientation;
// If this application is fullscreen, and didn't explicitly say
@@ -3712,6 +3761,7 @@ public class WindowManagerService extends IWindowManager.Stub
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
+ @Override
public Configuration updateOrientationFromAppTokens(
Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -3721,7 +3771,7 @@ public class WindowManagerService extends IWindowManager.Stub
Configuration config = null;
long ident = Binder.clearCallingIdentity();
-
+
synchronized(mWindowMap) {
config = updateOrientationFromAppTokensLocked(currentConfig,
freezeThisOneIfNeeded);
@@ -3762,7 +3812,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
-
+
return config;
}
@@ -3773,7 +3823,7 @@ public class WindowManagerService extends IWindowManager.Stub
* setNewConfiguration() TO TELL THE WINDOW MANAGER IT CAN UNFREEZE THE
* SCREEN. This will typically be done for you if you call
* sendNewConfiguration().
- *
+ *
* The orientation is computed from non-application windows first. If none of
* the non-application windows specify orientation, the orientation is computed from
* application tokens.
@@ -3810,6 +3860,7 @@ public class WindowManagerService extends IWindowManager.Stub
return req;
}
+ @Override
public void setNewConfiguration(Configuration config) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setNewConfiguration()")) {
@@ -3822,7 +3873,8 @@ public class WindowManagerService extends IWindowManager.Stub
performLayoutAndPlaceSurfacesLocked();
}
}
-
+
+ @Override
public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppOrientation()")) {
@@ -3971,14 +4023,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
- int startY, IRemoteCallback startedCallback, boolean delayed) {
+ int startY, IRemoteCallback startedCallback, boolean scaleUp) {
synchronized(mWindowMap) {
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
- mNextAppTransitionType = delayed
- ? ActivityOptions.ANIM_THUMBNAIL_DELAYED : ActivityOptions.ANIM_THUMBNAIL;
+ mNextAppTransitionType = scaleUp
+ ? ActivityOptions.ANIM_THUMBNAIL_SCALE_UP : ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
mNextAppTransitionPackage = null;
mNextAppTransitionThumbnail = srcThumb;
- mNextAppTransitionDelayed = delayed;
+ mNextAppTransitionScaleUp = scaleUp;
mNextAppTransitionStartX = startX;
mNextAppTransitionStartY = startY;
scheduleAnimationCallback(mNextAppTransitionCallback);
@@ -4075,7 +4127,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) {
Slog.v(TAG, "Removing starting window: " + startingWindow);
}
- mWindows.remove(startingWindow);
+ startingWindow.getWindowList().remove(startingWindow);
mWindowsChanged = true;
if (DEBUG_ADD_REMOVE) Slog.v(TAG,
"Removing starting " + startingWindow + " from " + ttoken);
@@ -4612,14 +4664,14 @@ public class WindowManagerService extends IWindowManager.Stub
for (int i=0; i<NW; i++) {
WindowState win = token.windows.get(i);
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing app window " + win);
- mWindows.remove(win);
+ win.getWindowList().remove(win);
int j = win.mChildWindows.size();
while (j > 0) {
j--;
WindowState cwin = win.mChildWindows.get(j);
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Tmp removing child window " + cwin);
- mWindows.remove(cwin);
+ cwin.getWindowList().remove(cwin);
}
}
return NW > 0;
@@ -4638,19 +4690,22 @@ public class WindowManagerService extends IWindowManager.Stub
}
void dumpWindowsLocked() {
- for (int i=mWindows.size()-1; i>=0; i--) {
- Slog.v(TAG, " #" + i + ": " + mWindows.get(i));
+ int i = 0;
+ final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
+ while (iterator.hasNext()) {
+ final WindowState w = iterator.next();
+ Slog.v(TAG, " #" + i++ + ": " + w);
}
}
- private int findWindowOffsetLocked(int tokenPos) {
- final int NW = mWindows.size();
+ private int findWindowOffsetLocked(WindowList windows, int tokenPos) {
+ final int NW = windows.size();
if (tokenPos >= mAnimatingAppTokens.size()) {
int i = NW;
while (i > 0) {
i--;
- WindowState win = mWindows.get(i);
+ WindowState win = windows.get(i);
if (win.getAppToken() != null) {
return i+1;
}
@@ -4660,7 +4715,7 @@ public class WindowManagerService extends IWindowManager.Stub
while (tokenPos > 0) {
// Find the first app token below the new position that has
// a window displayed.
- final AppWindowToken wtoken = mAnimatingAppTokens.get(tokenPos-1);
+ final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows @ "
+ tokenPos + " -- " + wtoken.token);
if (wtoken.sendingToBottom) {
@@ -4679,7 +4734,7 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState cwin = win.mChildWindows.get(j);
if (cwin.mSubLayer >= 0) {
for (int pos=NW-1; pos>=0; pos--) {
- if (mWindows.get(pos) == cwin) {
+ if (windows.get(pos) == cwin) {
if (DEBUG_REORDER) Slog.v(TAG,
"Found child win @" + (pos+1));
return pos+1;
@@ -4688,7 +4743,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
for (int pos=NW-1; pos>=0; pos--) {
- if (mWindows.get(pos) == win) {
+ if (windows.get(pos) == win) {
if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos+1));
return pos+1;
}
@@ -4701,6 +4756,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private final int reAddWindowLocked(int index, WindowState win) {
+ final WindowList windows = win.getWindowList();
final int NCW = win.mChildWindows.size();
boolean added = false;
for (int j=0; j<NCW; j++) {
@@ -4709,31 +4765,35 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at "
+ index + ": " + cwin);
win.mRebuilding = false;
- mWindows.add(index, win);
+ windows.add(index, win);
index++;
added = true;
}
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at "
+ index + ": " + cwin);
cwin.mRebuilding = false;
- mWindows.add(index, cwin);
+ windows.add(index, cwin);
index++;
}
if (!added) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at "
+ index + ": " + win);
win.mRebuilding = false;
- mWindows.add(index, win);
+ windows.add(index, win);
index++;
}
mWindowsChanged = true;
return index;
}
- private final int reAddAppWindowsLocked(int index, WindowToken token) {
+ private final int reAddAppWindowsLocked(final DisplayContent displayContent, int index,
+ WindowToken token) {
final int NW = token.windows.size();
for (int i=0; i<NW; i++) {
- index = reAddWindowLocked(index, token.windows.get(i));
+ final WindowState win = token.windows.get(i);
+ if (win.mDisplayContent == displayContent) {
+ index = reAddWindowLocked(index, token.windows.get(i));
+ }
}
return index;
}
@@ -4779,7 +4839,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (tmpRemoveAppWindowsLocked(wtoken)) {
if (DEBUG_REORDER) Slog.v(TAG, "Adding windows back in:");
if (DEBUG_REORDER) dumpWindowsLocked();
- reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while(iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ final WindowList windows = displayContent.getWindowList();
+ final int pos = findWindowOffsetLocked(windows, index);
+ reAddAppWindowsLocked(displayContent, pos, wtoken);
+ }
if (DEBUG_REORDER) Slog.v(TAG, "Final window list:");
if (DEBUG_REORDER) dumpWindowsLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4818,18 +4884,25 @@ public class WindowManagerService extends IWindowManager.Stub
// First remove all of the windows from the list.
tmpRemoveAppWindowsLocked(wtoken);
- // Where to start adding?
- int pos = findWindowOffsetLocked(tokenPos);
-
// And now add them back at the correct place.
- pos = reAddAppWindowsLocked(pos, wtoken);
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ final WindowList windows = displayContent.getWindowList();
+ final int pos = findWindowOffsetLocked(windows, tokenPos);
+ reAddAppWindowsLocked(displayContent, pos, wtoken);
+
+ if (updateFocusAndLayout && !updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
+ assignLayersLocked(windows);
+ }
+ }
if (updateFocusAndLayout) {
mInputMonitor.setUpdateInputWindowsNeededLw();
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
- assignLayersLocked();
- }
+
+ // Note that the above updateFocusedWindowLocked conditional used to sit here.
+
mLayoutNeeded = true;
if (!mInLayout) {
performLayoutAndPlaceSurfacesLocked();
@@ -4849,22 +4922,29 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // Where to start adding?
- int pos = findWindowOffsetLocked(tokenPos);
-
// And now add them back at the correct place.
- for (i=0; i<N; i++) {
- WindowToken token = mTokenMap.get(tokens.get(i));
- if (token != null) {
- pos = reAddAppWindowsLocked(pos, token);
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ final WindowList windows = displayContent.getWindowList();
+ // Where to start adding?
+ int pos = findWindowOffsetLocked(windows, tokenPos);
+ for (i=0; i<N; i++) {
+ WindowToken token = mTokenMap.get(tokens.get(i));
+ if (token != null) {
+ pos = reAddAppWindowsLocked(displayContent, pos, token);
+ }
+ }
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
+ assignLayersLocked(windows);
}
}
mInputMonitor.setUpdateInputWindowsNeededLw();
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
- assignLayersLocked();
- }
+
+ // Note that the above updateFocusedWindowLocked used to sit here.
+
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -4946,59 +5026,26 @@ public class WindowManagerService extends IWindowManager.Stub
// Misc IWindowSession methods
// -------------------------------------------------------------
- private boolean shouldAllowDisableKeyguard()
- {
- // We fail safe and prevent disabling keyguard in the unlikely event this gets
- // called before DevicePolicyManagerService has started.
- if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) {
- DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- if (dpm != null) {
- mAllowDisableKeyguard = dpm.getPasswordQuality(null)
- == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ?
- ALLOW_DISABLE_YES : ALLOW_DISABLE_NO;
- }
- }
- return mAllowDisableKeyguard == ALLOW_DISABLE_YES;
- }
-
+ @Override
public void disableKeyguard(IBinder token, String tag) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
- synchronized (mKeyguardTokenWatcher) {
- mKeyguardTokenWatcher.acquire(token, tag);
- }
+ mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
+ KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
}
+ @Override
public void reenableKeyguard(IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
- synchronized (mKeyguardTokenWatcher) {
- mKeyguardTokenWatcher.release(token);
-
- if (!mKeyguardTokenWatcher.isAcquired()) {
- // If we are the last one to reenable the keyguard wait until
- // we have actually finished reenabling until returning.
- // It is possible that reenableKeyguard() can be called before
- // the previous disableKeyguard() is handled, in which case
- // neither mKeyguardTokenWatcher.acquired() or released() would
- // be called. In that case mKeyguardDisabled will be false here
- // and we have nothing to wait for.
- while (mKeyguardDisabled) {
- try {
- mKeyguardTokenWatcher.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
- }
+ mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
+ KeyguardDisableHandler.KEYGUARD_REENABLE, token));
}
/**
@@ -5044,8 +5091,9 @@ public class WindowManagerService extends IWindowManager.Stub
public void closeSystemDialogs(String reason) {
synchronized(mWindowMap) {
- for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = mWindows.get(i);
+ final AllWindowsIterator iterator = new AllWindowsIterator();
+ while (iterator.hasNext()) {
+ final WindowState w = iterator.next();
if (w.mHasSurface) {
try {
w.mClient.closeSystemDialogs(reason);
@@ -5095,7 +5143,7 @@ public class WindowManagerService extends IWindowManager.Stub
mTransitionAnimationScale = fixScale(scales[1]);
}
if (scales.length >= 3) {
- mAnimatorDurationScale = fixScale(scales[2]);
+ setAnimatorDurationScale(fixScale(scales[2]));
}
}
@@ -5103,6 +5151,11 @@ public class WindowManagerService extends IWindowManager.Stub
mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
}
+ private void setAnimatorDurationScale(float scale) {
+ mAnimatorDurationScale = scale;
+ ValueAnimator.setDurationScale(scale);
+ }
+
public float getAnimationScale(int which) {
switch (which) {
case 0: return mWindowAnimationScale;
@@ -5148,17 +5201,20 @@ public class WindowManagerService extends IWindowManager.Stub
// Called by window manager policy. Not exposed externally.
@Override
- public void shutdown() {
- ShutdownThread.shutdown(mContext, true);
+ public void shutdown(boolean confirm) {
+ ShutdownThread.shutdown(mContext, confirm);
}
// Called by window manager policy. Not exposed externally.
@Override
- public void rebootSafeMode() {
- ShutdownThread.rebootSafeMode(mContext, true);
+ public void rebootSafeMode(boolean confirm) {
+ ShutdownThread.rebootSafeMode(mContext, confirm);
}
- public void setInputFilter(InputFilter filter) {
+ public void setInputFilter(IInputFilter filter) {
+ if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) {
+ throw new SecurityException("Requires FILTER_EVENTS permission");
+ }
mInputManager.setInputFilter(filter);
}
@@ -5247,9 +5303,11 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.bool.config_enableWallpaperService)
&& !mOnlyCore;
boolean haveKeyguard = true;
- final int N = mWindows.size();
+ // TODO(multidisplay): Expand to all displays?
+ final WindowList windows = getDefaultWindowList();
+ final int N = windows.size();
for (int i=0; i<N; i++) {
- WindowState w = mWindows.get(i);
+ WindowState w = windows.get(i);
if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
// Only if there is a keyguard attached to the window manager
// will we consider ourselves as having a keyguard. If it
@@ -5401,8 +5459,9 @@ public class WindowManagerService extends IWindowManager.Stub
// the background..)
if (on) {
boolean isVisible = false;
- for (int i = mWindows.size() - 1; i >= 0; i--) {
- final WindowState ws = mWindows.get(i);
+ final AllWindowsIterator iterator = new AllWindowsIterator();
+ while (iterator.hasNext()) {
+ final WindowState ws = iterator.next();
if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
isVisible = true;
break;
@@ -5418,7 +5477,7 @@ public class WindowManagerService extends IWindowManager.Stub
Surface.openTransaction();
try {
if (mStrictModeFlash == null) {
- mStrictModeFlash = new StrictModeFlash(mDisplay, mFxSession);
+ mStrictModeFlash = new StrictModeFlash(mDefaultDisplay, mFxSession);
}
mStrictModeFlash.setVisibility(on);
} finally {
@@ -5438,10 +5497,11 @@ public class WindowManagerService extends IWindowManager.Stub
* In portrait mode, it grabs the upper region of the screen based on the vertical dimension
* of the target image.
*
+ * @param displayId the Display to take a screenshot of.
* @param width the width of the target bitmap
* @param height the height of the target bitmap
*/
- public Bitmap screenshotApplications(IBinder appToken, int width, int height) {
+ public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height) {
if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"screenshotApplications()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
@@ -5459,8 +5519,10 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
long ident = Binder.clearCallingIdentity();
- dw = mCurDisplayWidth;
- dh = mCurDisplayHeight;
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ dw = displayInfo.logicalWidth;
+ dh = displayInfo.logicalHeight;
int aboveAppLayer = mPolicy.windowTypeToLayerLw(
WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER
@@ -5474,8 +5536,9 @@ public class WindowManagerService extends IWindowManager.Stub
// Figure out the part of the screen that is actually the app.
boolean including = false;
- for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState ws = mWindows.get(i);
+ final WindowList windows = displayContent.getWindowList();
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState ws = windows.get(i);
if (!ws.mHasSurface) {
continue;
}
@@ -5527,7 +5590,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
// The screenshot API does not apply the current screen rotation.
- rot = mDisplay.getRotation();
+ rot = mDefaultDisplay.getRotation();
int fw = frame.width();
int fh = frame.height();
@@ -5562,10 +5625,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (DEBUG_SCREENSHOT) {
Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer);
- for (int i=0; i<mWindows.size(); i++) {
- Slog.i(TAG, mWindows.get(i) + ": " + mWindows.get(i).mLayer
- + " animLayer=" + mWindows.get(i).mWinAnimator.mAnimLayer
- + " surfaceLayer=" + mWindows.get(i).mWinAnimator.mSurfaceLayer);
+ for (int i = 0; i < windows.size(); i++) {
+ WindowState win = windows.get(i);
+ Slog.i(TAG, win + ": " + win.mLayer
+ + " animLayer=" + win.mWinAnimator.mAnimLayer
+ + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
}
}
rawss = Surface.screenshot(dw, dh, 0, maxLayer);
@@ -5687,6 +5751,7 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
}
+ // TODO(multidisplay): Rotate any display?
/**
* Updates the current rotation.
*
@@ -5754,8 +5819,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWaitingForConfig = true;
mLayoutNeeded = true;
startFreezingDisplayLocked(inTransaction);
- mInputManager.setDisplayOrientation(0, rotation,
- mDisplay != null ? mDisplay.getExternalRotation() : Surface.ROTATION_0);
+ mInputManager.setDisplayOrientation(0, rotation, Surface.ROTATION_0);
// We need to update our screen size information to match the new
// rotation. Note that this is redundant with the later call to
@@ -5770,6 +5834,8 @@ public class WindowManagerService extends IWindowManager.Stub
">>> OPEN TRANSACTION setRotationUnchecked");
Surface.openTransaction();
}
+ final DisplayContent displayContent = getDefaultDisplayContent();
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
try {
// NOTE: We disable the rotation in the emulator because
// it doesn't support hardware OpenGL emulation yet.
@@ -5777,8 +5843,8 @@ public class WindowManagerService extends IWindowManager.Stub
&& mAnimator.mScreenRotationAnimation.hasScreenshot()) {
if (mAnimator.mScreenRotationAnimation.setRotation(rotation, mFxSession,
MAX_ANIMATION_DURATION, mTransitionAnimationScale,
- mCurDisplayWidth, mCurDisplayHeight)) {
- scheduleAnimationLocked();
+ displayInfo.logicalWidth, displayInfo.logicalHeight)) {
+ updateLayoutToAnimationLocked();
}
}
Surface.setOrientation(0, rotation);
@@ -5790,10 +5856,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- rebuildBlackFrame();
+ rebuildBlackFrameLocked();
- for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = mWindows.get(i);
+ final WindowList windows = displayContent.getWindowList();
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
if (w.mHasSurface) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
w.mOrientationChanging = true;
@@ -5864,7 +5931,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
final int rotation = getRotation();
- if (mInitialDisplayWidth < mInitialDisplayHeight) {
+ // TODO(multidisplay): Assume that such devices physical keys are on the main screen.
+ final DisplayContent displayContent = getDefaultDisplayContent();
+ if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) {
// On devices with a natural orientation of portrait
switch (rotation) {
default:
@@ -5875,7 +5944,7 @@ public class WindowManagerService extends IWindowManager.Stub
case Surface.ROTATION_180:
return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
case Surface.ROTATION_270:
- return Gravity.LEFT | Gravity.BOTTOM;
+ return Gravity.START | Gravity.BOTTOM;
}
} else {
// On devices with a natural orientation of landscape
@@ -5886,7 +5955,7 @@ public class WindowManagerService extends IWindowManager.Stub
case Surface.ROTATION_90:
return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
case Surface.ROTATION_180:
- return Gravity.LEFT | Gravity.BOTTOM;
+ return Gravity.START | Gravity.BOTTOM;
case Surface.ROTATION_270:
return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
}
@@ -6000,10 +6069,13 @@ public class WindowManagerService extends IWindowManager.Stub
boolean result = true;
- WindowState[] windows;
+ WindowList windows = new WindowList();
synchronized (mWindowMap) {
//noinspection unchecked
- windows = mWindows.toArray(new WindowState[mWindows.size()]);
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while(iterator.hasNext()) {
+ windows.addAll(iterator.next().getWindowList());
+ }
}
BufferedWriter out = null;
@@ -6013,9 +6085,9 @@ public class WindowManagerService extends IWindowManager.Stub
OutputStream clientStream = client.getOutputStream();
out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
- final int count = windows.length;
+ final int count = windows.size();
for (int i = 0; i < count; i++) {
- final WindowState w = windows[i];
+ final WindowState w = windows.get(i);
out.write(Integer.toHexString(System.identityHashCode(w)));
out.write(' ');
out.append(w.mAttrs.getTitle());
@@ -6039,6 +6111,7 @@ public class WindowManagerService extends IWindowManager.Stub
return result;
}
+ // TODO(multidisplay): Extend to multiple displays.
/**
* Returns the focused window in the following format:
* windowHashCodeInHexadecimal windowName
@@ -6224,15 +6297,14 @@ public class WindowManagerService extends IWindowManager.Stub
private WindowState findWindow(int hashCode) {
if (hashCode == -1) {
+ // TODO(multidisplay): Extend to multiple displays.
return getFocusedWindow();
}
synchronized (mWindowMap) {
- final ArrayList<WindowState> windows = mWindows;
- final int count = windows.size();
-
- for (int i = 0; i < count; i++) {
- WindowState w = windows.get(i);
+ final AllWindowsIterator iterator = new AllWindowsIterator();
+ while (iterator.hasNext()) {
+ final WindowState w = iterator.next();
if (System.identityHashCode(w) == hashCode) {
return w;
}
@@ -6274,20 +6346,20 @@ public class WindowManagerService extends IWindowManager.Stub
return config;
}
- private void adjustDisplaySizeRanges(int rotation, int dw, int dh) {
+ private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) {
final int width = mPolicy.getConfigDisplayWidth(dw, dh, rotation);
- if (width < mSmallestDisplayWidth) {
- mSmallestDisplayWidth = width;
+ if (width < displayInfo.smallestNominalAppWidth) {
+ displayInfo.smallestNominalAppWidth = width;
}
- if (width > mLargestDisplayWidth) {
- mLargestDisplayWidth = width;
+ if (width > displayInfo.largestNominalAppWidth) {
+ displayInfo.largestNominalAppWidth = width;
}
final int height = mPolicy.getConfigDisplayHeight(dw, dh, rotation);
- if (height < mSmallestDisplayHeight) {
- mSmallestDisplayHeight = height;
+ if (height < displayInfo.smallestNominalAppHeight) {
+ displayInfo.smallestNominalAppHeight = height;
}
- if (height > mLargestDisplayHeight) {
- mLargestDisplayHeight = height;
+ if (height > displayInfo.largestNominalAppHeight) {
+ displayInfo.largestNominalAppHeight = height;
}
}
@@ -6370,8 +6442,8 @@ public class WindowManagerService extends IWindowManager.Stub
return curLayout;
}
- private void computeSizeRangesAndScreenLayout(boolean rotated, int dw, int dh,
- float density, Configuration outConfig) {
+ private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated,
+ int dw, int dh, float density, Configuration outConfig) {
// We need to determine the smallest width that will occur under normal
// operation. To this, start with the base screen size and compute the
// width under the different possible rotations. We need to un-rotate
@@ -6384,21 +6456,21 @@ public class WindowManagerService extends IWindowManager.Stub
unrotDw = dw;
unrotDh = dh;
}
- mSmallestDisplayWidth = 1<<30;
- mSmallestDisplayHeight = 1<<30;
- mLargestDisplayWidth = 0;
- mLargestDisplayHeight = 0;
- adjustDisplaySizeRanges(Surface.ROTATION_0, unrotDw, unrotDh);
- adjustDisplaySizeRanges(Surface.ROTATION_90, unrotDh, unrotDw);
- adjustDisplaySizeRanges(Surface.ROTATION_180, unrotDw, unrotDh);
- adjustDisplaySizeRanges(Surface.ROTATION_270, unrotDh, unrotDw);
+ displayInfo.smallestNominalAppWidth = 1<<30;
+ displayInfo.smallestNominalAppHeight = 1<<30;
+ displayInfo.largestNominalAppWidth = 0;
+ displayInfo.largestNominalAppHeight = 0;
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, unrotDw, unrotDh);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, unrotDh, unrotDw);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, unrotDw, unrotDh);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, unrotDh, unrotDw);
int sl = Configuration.SCREENLAYOUT_SIZE_XLARGE
| Configuration.SCREENLAYOUT_LONG_YES;
sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh);
sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw);
sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh);
sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw);
- outConfig.smallestScreenWidthDp = (int)(mSmallestDisplayWidth / density);
+ outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
outConfig.screenLayout = sl;
}
@@ -6433,42 +6505,39 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean computeScreenConfigurationLocked(Configuration config) {
- if (mDisplay == null) {
+ if (mDefaultDisplay == null) {
return false;
}
+ // TODO(multidisplay): For now, apply Configuration to main screen only.
+ final DisplayContent displayContent = getDefaultDisplayContent();
+
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
|| mRotation == Surface.ROTATION_270);
- final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
- final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
-
- synchronized(mDisplaySizeLock) {
- if (mAltOrientation) {
- mCurDisplayWidth = realdw;
- mCurDisplayHeight = realdh;
- if (realdw > realdh) {
- // Turn landscape into portrait.
- int maxw = (int)(realdh/1.3f);
- if (maxw < realdw) {
- mCurDisplayWidth = maxw;
- }
- } else {
- // Turn portrait into landscape.
- int maxh = (int)(realdw/1.3f);
- if (maxh < realdh) {
- mCurDisplayHeight = maxh;
- }
+ final int realdw = rotated ?
+ displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;
+ final int realdh = rotated ?
+ displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight;
+ int dw = realdw;
+ int dh = realdh;
+
+ if (mAltOrientation) {
+ if (realdw > realdh) {
+ // Turn landscape into portrait.
+ int maxw = (int)(realdh/1.3f);
+ if (maxw < realdw) {
+ dw = maxw;
}
} else {
- mCurDisplayWidth = realdw;
- mCurDisplayHeight = realdh;
+ // Turn portrait into landscape.
+ int maxh = (int)(realdw/1.3f);
+ if (maxh < realdh) {
+ dh = maxh;
+ }
}
}
- final int dw = mCurDisplayWidth;
- final int dh = mCurDisplayHeight;
-
if (config != null) {
int orientation = Configuration.ORIENTATION_SQUARE;
if (dw < dh) {
@@ -6479,25 +6548,28 @@ public class WindowManagerService extends IWindowManager.Stub
config.orientation = orientation;
}
- // Update real display metrics.
- mDisplay.getMetricsWithSize(mRealDisplayMetrics, mCurDisplayWidth, mCurDisplayHeight);
-
// Update application display metrics.
- final DisplayMetrics dm = mDisplayMetrics;
final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
- synchronized(mDisplaySizeLock) {
- mAppDisplayWidth = appWidth;
- mAppDisplayHeight = appHeight;
- mAnimator.setDisplayDimensions(mCurDisplayWidth, mCurDisplayHeight,
- mAppDisplayWidth, mAppDisplayHeight);
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ synchronized(displayContent.mDisplaySizeLock) {
+ displayInfo.rotation = mRotation;
+ displayInfo.logicalWidth = dw;
+ displayInfo.logicalHeight = dh;
+ displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
+ displayInfo.appWidth = appWidth;
+ displayInfo.appHeight = appHeight;
+ displayInfo.getLogicalMetrics(mRealDisplayMetrics, null);
+ displayInfo.getAppMetrics(mDisplayMetrics, null);
+ mDisplayManagerService.setDisplayInfo(displayContent.getDisplayId(), displayInfo);
+
+ mAnimator.setDisplayDimensions(dw, dh, appWidth, appHeight);
}
if (false) {
- Slog.i(TAG, "Set app display size: " + mAppDisplayWidth
- + " x " + mAppDisplayHeight);
+ Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight);
}
- mDisplay.getMetricsWithSize(dm, mAppDisplayWidth, mAppDisplayHeight);
+ final DisplayMetrics dm = mDisplayMetrics;
mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm,
mCompatDisplayMetrics);
@@ -6506,11 +6578,12 @@ public class WindowManagerService extends IWindowManager.Stub
/ dm.density);
config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation)
/ dm.density);
- computeSizeRangesAndScreenLayout(rotated, dw, dh, dm.density, config);
+ computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh, dm.density, config);
config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);
+ config.densityDpi = displayContent.mBaseDisplayDensity;
// Update the configuration based on available input devices, lid switch,
// and platform configuration.
@@ -6638,7 +6711,8 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
try {
if (mDragState == null) {
- Surface surface = new Surface(session, callerPid, "drag surface", 0,
+ Surface surface = new Surface(session, callerPid, "drag surface",
+ mDefaultDisplay.getLayerStack(),
width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG "
+ surface + ": CREATE");
@@ -6720,8 +6794,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // TODO: Put this on the IWindowManagerService and guard with a permission.
- public IBinder getFocusedWindowClientToken() {
+ public IBinder getFocusedWindowToken() {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "getFocusedWindowToken()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+ }
synchronized (mWindowMap) {
WindowState windowState = getFocusedWindowLocked();
if (windowState != null) {
@@ -6731,8 +6808,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // TODO: This is a workaround - remove when 6623031 is fixed.
public boolean getWindowFrame(IBinder token, Rect outBounds) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "getWindowFrame()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+ }
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(token);
if (windowState != null) {
@@ -6790,45 +6870,57 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void displayReady() {
+ WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+ final Display display = wm.getDefaultDisplay();
+ displayReady(display.getDisplayId());
+
synchronized(mWindowMap) {
- if (mDisplay != null) {
- throw new IllegalStateException("Display already initialized");
- }
- WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- mDisplay = wm.getDefaultDisplay();
+ readForcedDisplaySizeAndDensityLocked(getDefaultDisplayContent());
+
+ mDefaultDisplay = display;
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_TOUCHSCREEN);
- synchronized(mDisplaySizeLock) {
- mInitialDisplayWidth = mDisplay.getRawWidth();
- mInitialDisplayHeight = mDisplay.getRawHeight();
- int rot = mDisplay.getRotation();
- if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
- // If the screen is currently rotated, we need to swap the
- // initial width and height to get the true natural values.
- int tmp = mInitialDisplayWidth;
- mInitialDisplayWidth = mInitialDisplayHeight;
- mInitialDisplayHeight = tmp;
- }
- mBaseDisplayWidth = mCurDisplayWidth = mAppDisplayWidth = mInitialDisplayWidth;
- mBaseDisplayHeight = mCurDisplayHeight = mAppDisplayHeight = mInitialDisplayHeight;
- mAnimator.setDisplayDimensions(mCurDisplayWidth, mCurDisplayHeight,
- mAppDisplayWidth, mAppDisplayHeight);
- }
- mInputManager.setDisplaySize(Display.DEFAULT_DISPLAY,
- mDisplay.getRawWidth(), mDisplay.getRawHeight(),
- mDisplay.getRawExternalWidth(), mDisplay.getRawExternalHeight());
- mInputManager.setDisplayOrientation(Display.DEFAULT_DISPLAY,
- mDisplay.getRotation(), mDisplay.getExternalRotation());
- mPolicy.setInitialDisplaySize(mDisplay, mInitialDisplayWidth, mInitialDisplayHeight);
+ PackageManager.FEATURE_TOUCHSCREEN);
+
+ mAnimator.initializeLocked(display.getLayerStack());
+
+ final DisplayInfo displayInfo = getDefaultDisplayInfo();
+ mAnimator.setDisplayDimensions(displayInfo.logicalWidth, displayInfo.logicalHeight,
+ displayInfo.appWidth, displayInfo.appHeight);
+
+ DisplayDeviceInfo info = new DisplayDeviceInfo();
+ mDisplayManagerService.getDefaultExternalDisplayDeviceInfo(info);
+
+ final DisplayContent displayContent = getDefaultDisplayContent();
+ mInputManager.setDisplaySize(displayContent.getDisplayId(),
+ displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight,
+ info.width, info.height);
+ mInputManager.setDisplayOrientation(displayContent.getDisplayId(),
+ mDefaultDisplay.getRotation(), Surface.ROTATION_0);
+ mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mInitialDisplayWidth,
+ displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity);
}
try {
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
-
- synchronized (mWindowMap) {
- readForcedDisplaySizeLocked();
+ }
+
+ public void displayReady(int displayId) {
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ final DisplayInfo displayInfo;
+ synchronized(displayContent.mDisplaySizeLock) {
+ // Bootstrap the default logical display from the display manager.
+ displayInfo = displayContent.getDisplayInfo();
+ mDisplayManagerService.getDisplayInfo(displayId, displayInfo);
+ displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
+ displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
+ displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
+ displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
+ displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;
+ displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
+ }
}
}
@@ -6836,14 +6928,13 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.systemReady();
}
+ // TODO(multidisplay): Call isScreenOn for each display.
private void sendScreenStatusToClientsLocked() {
- final ArrayList<WindowState> windows = mWindows;
- final int count = windows.size();
- boolean on = mPowerManager.isScreenOn();
- for (int i = count - 1; i >= 0; i--) {
- WindowState win = mWindows.get(i);
+ final boolean on = mPowerManager.isScreenOn();
+ final AllWindowsIterator iterator = new AllWindowsIterator();
+ while (iterator.hasNext()) {
try {
- win.mClient.dispatchScreenState(on);
+ iterator.next().mClient.dispatchScreenState(on);
} catch (RemoteException e) {
// Ignored
}
@@ -6864,7 +6955,7 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int REPORT_APPLICATION_TOKEN_WINDOWS = 8;
public static final int REPORT_APPLICATION_TOKEN_DRAWN = 9;
public static final int WINDOW_FREEZE_TIMEOUT = 11;
- public static final int HOLD_SCREEN_CHANGED = 12;
+
public static final int APP_TRANSITION_TIMEOUT = 13;
public static final int PERSIST_ANIMATION_SCALE = 14;
public static final int FORCE_GC = 15;
@@ -6877,17 +6968,13 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
public static final int BOOT_TIMEOUT = 23;
public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
- public static final int BULK_UPDATE_PARAMETERS = 25;
+ public static final int UPDATE_ANIM_PARAMETERS = 25;
public static final int SHOW_STRICT_MODE_VIOLATION = 26;
public static final int DO_ANIMATION_CALLBACK = 27;
public static final int ANIMATOR_WHAT_OFFSET = 100000;
public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
- public static final int SET_WALLPAPER_OFFSET = ANIMATOR_WHAT_OFFSET + 2;
- public static final int SET_DIM_PARAMETERS = ANIMATOR_WHAT_OFFSET + 3;
- public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 4;
-
- private Session mLastReportedHold;
+ public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 2;
public H() {
}
@@ -7122,12 +7209,14 @@ public class WindowManagerService extends IWindowManager.Stub
} break;
case WINDOW_FREEZE_TIMEOUT: {
+ // TODO(multidisplay): Can non-default displays rotate?
synchronized (mWindowMap) {
Slog.w(TAG, "Window freeze timeout expired.");
- int i = mWindows.size();
+ final WindowList windows = getDefaultWindowList();
+ int i = windows.size();
while (i > 0) {
i--;
- WindowState w = mWindows.get(i);
+ WindowState w = windows.get(i);
if (w.mOrientationChanging) {
w.mOrientationChanging = false;
Slog.w(TAG, "Force clearing orientation change: " + w);
@@ -7138,33 +7227,6 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case HOLD_SCREEN_CHANGED: {
- Session oldHold;
- Session newHold;
- synchronized (mWindowMap) {
- oldHold = mLastReportedHold;
- newHold = (Session)msg.obj;
- mLastReportedHold = newHold;
- }
-
- if (oldHold != newHold) {
- try {
- if (oldHold != null) {
- mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
- "window",
- BatteryStats.WAKE_TYPE_WINDOW);
- }
- if (newHold != null) {
- mBatteryStats.noteStartWakelock(newHold.mUid, -1,
- "window",
- BatteryStats.WAKE_TYPE_WINDOW);
- }
- } catch (RemoteException e) {
- }
- }
- break;
- }
-
case APP_TRANSITION_TIMEOUT: {
synchronized (mWindowMap) {
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
@@ -7191,18 +7253,22 @@ public class WindowManagerService extends IWindowManager.Stub
}
case FORCE_GC: {
- synchronized(mWindowMap) {
- if (mAnimationScheduled) {
- // If we are animating, don't do the gc now but
- // delay a bit so we don't interrupt the animation.
- mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
- 2000);
- return;
- }
- // If we are currently rotating the display, it will
- // schedule a new message when done.
- if (mDisplayFrozen) {
- return;
+ synchronized (mWindowMap) {
+ synchronized (mAnimator) {
+ // Since we're holding both mWindowMap and mAnimator we don't need to
+ // hold mAnimator.mLayoutToAnim.
+ if (mAnimator.mAnimating || mLayoutToAnim.mAnimationScheduled) {
+ // If we are animating, don't do the gc now but
+ // delay a bit so we don't interrupt the animation.
+ mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
+ 2000);
+ return;
+ }
+ // If we are currently rotating the display, it will
+ // schedule a new message when done.
+ if (mDisplayFrozen) {
+ return;
+ }
}
}
Runtime.getRuntime().gc();
@@ -7306,42 +7372,10 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case BULK_UPDATE_PARAMETERS: {
+ case UPDATE_ANIM_PARAMETERS: {
// Used to send multiple changes from the animation side to the layout side.
synchronized (mWindowMap) {
- boolean doRequest = false;
- // TODO(cmautner): As the number of bits grows, use masks of bit groups to
- // eliminate unnecessary tests.
- if ((msg.arg1 & LayoutFields.SET_UPDATE_ROTATION) != 0) {
- mInnerFields.mUpdateRotation = true;
- doRequest = true;
- }
- if ((msg.arg1 & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) {
- mInnerFields.mWallpaperMayChange = true;
- doRequest = true;
- }
- if ((msg.arg1 & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) {
- mInnerFields.mWallpaperForceHidingChanged = true;
- doRequest = true;
- }
- if ((msg.arg1 & LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE) != 0) {
- mInnerFields.mOrientationChangeComplete = false;
- } else {
- mInnerFields.mOrientationChangeComplete = true;
- if (mWindowsFreezingScreen) {
- doRequest = true;
- }
- }
- if ((msg.arg1 & LayoutFields.SET_TURN_ON_SCREEN) != 0) {
- mTurnOnScreen = true;
- }
-
- mPendingLayoutChanges |= msg.arg2;
- if (mPendingLayoutChanges != 0) {
- doRequest = true;
- }
-
- if (doRequest) {
+ if (copyAnimToLayoutParamsLocked()) {
mH.sendEmptyMessage(CLEAR_PENDING_ACTIONS);
performLayoutAndPlaceSurfacesLocked();
}
@@ -7363,21 +7397,6 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case SET_WALLPAPER_OFFSET: {
- final WindowStateAnimator winAnimator = (WindowStateAnimator) msg.obj;
- winAnimator.setWallpaperOffset(msg.arg1, msg.arg2);
-
- scheduleAnimationLocked();
- break;
- }
-
- case SET_DIM_PARAMETERS: {
- mAnimator.mDimParams = (DimAnimator.Parameters) msg.obj;
-
- scheduleAnimationLocked();
- break;
- }
-
case CLEAR_PENDING_ACTIONS: {
mAnimator.clearPendingActions();
break;
@@ -7416,13 +7435,13 @@ public class WindowManagerService extends IWindowManager.Stub
// The focus for the client is the window immediately below
// where we would place the input method window.
int idx = findDesiredInputMethodWindowIndexLocked(false);
- WindowState imFocus;
if (idx > 0) {
- imFocus = mWindows.get(idx-1);
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ WindowState imFocus = getDefaultWindowList().get(idx-1);
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG, "Desired input method target: " + imFocus);
- Slog.i(TAG, "Current focus: " + this.mCurrentFocus);
- Slog.i(TAG, "Last focus: " + this.mLastFocus);
+ Slog.i(TAG, "Current focus: " + mCurrentFocus);
+ Slog.i(TAG, "Last focus: " + mLastFocus);
}
if (imFocus != null) {
// This may be a starting window, in which case we still want
@@ -7452,141 +7471,191 @@ public class WindowManagerService extends IWindowManager.Stub
imFocus.mSession.mClient.asBinder() == client.asBinder()) {
return true;
}
-
- // Okay, how about this... what is the current focus?
- // It seems in some cases we may not have moved the IM
- // target window, such as when it was in a pop-up window,
- // so let's also look at the current focus. (An example:
- // go to Gmail, start searching so the keyboard goes up,
- // press home. Sometimes the IME won't go down.)
- // Would be nice to fix this more correctly, but it's
- // way at the end of a release, and this should be good enough.
- if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null &&
- mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
- return true;
- }
}
}
- }
- return false;
- }
-
- public void getDisplaySize(Point size) {
- synchronized(mDisplaySizeLock) {
- size.x = mAppDisplayWidth;
- size.y = mAppDisplayHeight;
- }
- }
-
- public void getRealDisplaySize(Point size) {
- synchronized(mDisplaySizeLock) {
- size.x = mCurDisplayWidth;
- size.y = mCurDisplayHeight;
- }
- }
- public void getInitialDisplaySize(Point size) {
- synchronized(mDisplaySizeLock) {
- size.x = mInitialDisplayWidth;
- size.y = mInitialDisplayHeight;
- }
- }
-
- public int getMaximumSizeDimension() {
- synchronized(mDisplaySizeLock) {
- // Do this based on the raw screen size, until we are smarter.
- return mBaseDisplayWidth > mBaseDisplayHeight
- ? mBaseDisplayWidth : mBaseDisplayHeight;
+ // Okay, how about this... what is the current focus?
+ // It seems in some cases we may not have moved the IM
+ // target window, such as when it was in a pop-up window,
+ // so let's also look at the current focus. (An example:
+ // go to Gmail, start searching so the keyboard goes up,
+ // press home. Sometimes the IME won't go down.)
+ // Would be nice to fix this more correctly, but it's
+ // way at the end of a release, and this should be good enough.
+ if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
+ && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
+ return true;
+ }
}
+ return false;
}
- public void getCurrentSizeRange(Point smallestSize, Point largestSize) {
- synchronized(mDisplaySizeLock) {
- smallestSize.x = mSmallestDisplayWidth;
- smallestSize.y = mSmallestDisplayHeight;
- largestSize.x = mLargestDisplayWidth;
- largestSize.y = mLargestDisplayHeight;
+ public void getInitialDisplaySize(int displayId, Point size) {
+ // TODO(cmautner): Access to DisplayContent should be locked on mWindowMap. Doing that
+ // could lead to deadlock since this is called from ActivityManager.
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ synchronized(displayContent.mDisplaySizeLock) {
+ size.x = displayContent.mInitialDisplayWidth;
+ size.y = displayContent.mInitialDisplayHeight;
}
}
- public void setForcedDisplaySize(int longDimen, int shortDimen) {
+ public void setForcedDisplaySize(int displayId, int longDimen, int shortDimen) {
synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContent(displayId);
int width, height;
- if (mInitialDisplayWidth < mInitialDisplayHeight) {
- width = shortDimen < mInitialDisplayWidth
- ? shortDimen : mInitialDisplayWidth;
- height = longDimen < mInitialDisplayHeight
- ? longDimen : mInitialDisplayHeight;
+ if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) {
+ width = shortDimen < displayContent.mInitialDisplayWidth
+ ? shortDimen : displayContent.mInitialDisplayWidth;
+ height = longDimen < displayContent.mInitialDisplayHeight
+ ? longDimen : displayContent.mInitialDisplayHeight;
} else {
- width = longDimen < mInitialDisplayWidth
- ? longDimen : mInitialDisplayWidth;
- height = shortDimen < mInitialDisplayHeight
- ? shortDimen : mInitialDisplayHeight;
+ width = longDimen < displayContent.mInitialDisplayWidth
+ ? longDimen : displayContent.mInitialDisplayWidth;
+ height = shortDimen < displayContent.mInitialDisplayHeight
+ ? shortDimen : displayContent.mInitialDisplayHeight;
}
- setForcedDisplaySizeLocked(width, height);
+ setForcedDisplaySizeLocked(displayContent, width, height);
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.DISPLAY_SIZE_FORCED, width + "," + height);
}
}
- private void rebuildBlackFrame() {
+ private void rebuildBlackFrameLocked() {
if (mBlackFrame != null) {
mBlackFrame.kill();
mBlackFrame = null;
}
- if (mBaseDisplayWidth < mInitialDisplayWidth
- || mBaseDisplayHeight < mInitialDisplayHeight) {
+ // TODO(multidisplay): For now rotations are only main screen.
+ final DisplayContent displayContent = getDefaultDisplayContent();
+ if (displayContent.mBaseDisplayWidth < displayContent.mInitialDisplayWidth
+ || displayContent.mBaseDisplayHeight < displayContent.mInitialDisplayHeight) {
int initW, initH, baseW, baseH;
final boolean rotated = (mRotation == Surface.ROTATION_90
|| mRotation == Surface.ROTATION_270);
+ if (DEBUG_BOOT) {
+ Slog.i(TAG, "BLACK FRAME: rotated=" + rotated + " init="
+ + displayContent.mInitialDisplayWidth + "x"
+ + displayContent.mInitialDisplayHeight + " base="
+ + displayContent.mBaseDisplayWidth + "x"
+ + displayContent.mBaseDisplayHeight);
+ }
if (rotated) {
- initW = mInitialDisplayHeight;
- initH = mInitialDisplayWidth;
- baseW = mBaseDisplayHeight;
- baseH = mBaseDisplayWidth;
+ initW = displayContent.mInitialDisplayHeight;
+ initH = displayContent.mInitialDisplayWidth;
+ baseW = displayContent.mBaseDisplayHeight;
+ baseH = displayContent.mBaseDisplayWidth;
} else {
- initW = mInitialDisplayWidth;
- initH = mInitialDisplayHeight;
- baseW = mBaseDisplayWidth;
- baseH = mBaseDisplayHeight;
+ initW = displayContent.mInitialDisplayWidth;
+ initH = displayContent.mInitialDisplayHeight;
+ baseW = displayContent.mBaseDisplayWidth;
+ baseH = displayContent.mBaseDisplayHeight;
}
Rect outer = new Rect(0, 0, initW, initH);
Rect inner = new Rect(0, 0, baseW, baseH);
try {
- mBlackFrame = new BlackFrame(mFxSession, outer, inner, MASK_LAYER);
+ mBlackFrame = new BlackFrame(mFxSession, outer, inner, MASK_LAYER,
+ mDefaultDisplay.getLayerStack());
} catch (Surface.OutOfResourcesException e) {
}
}
}
- private void readForcedDisplaySizeLocked() {
- final String str = Settings.Secure.getString(mContext.getContentResolver(),
+ private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
+ boolean changed = false;
+ final String sizeStr = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.DISPLAY_SIZE_FORCED);
- if (str == null || str.length() == 0) {
- return;
+ if (sizeStr != null && sizeStr.length() > 0) {
+ final int pos = sizeStr.indexOf(',');
+ if (pos > 0 && sizeStr.lastIndexOf(',') == pos) {
+ int width, height;
+ try {
+ width = Integer.parseInt(sizeStr.substring(0, pos));
+ height = Integer.parseInt(sizeStr.substring(pos+1));
+ synchronized(displayContent.mDisplaySizeLock) {
+ if (displayContent.mBaseDisplayWidth != width
+ || displayContent.mBaseDisplayHeight != height) {
+ changed = true;
+ Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height);
+ displayContent.mBaseDisplayWidth = width;
+ displayContent.mBaseDisplayHeight = height;
+ }
+ }
+ } catch (NumberFormatException ex) {
+ }
+ }
}
- final int pos = str.indexOf(',');
- if (pos <= 0 || str.lastIndexOf(',') != pos) {
- return;
+ final String densityStr = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.DISPLAY_DENSITY_FORCED);
+ if (densityStr != null && densityStr.length() > 0) {
+ int density;
+ try {
+ density = Integer.parseInt(densityStr);
+ synchronized(displayContent.mDisplaySizeLock) {
+ if (displayContent.mBaseDisplayDensity != density) {
+ changed = true;
+ Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density);
+ displayContent.mBaseDisplayDensity = density;
+ }
+ }
+ } catch (NumberFormatException ex) {
+ }
}
- int width, height;
- try {
- width = Integer.parseInt(str.substring(0, pos));
- height = Integer.parseInt(str.substring(pos+1));
- } catch (NumberFormatException ex) {
- return;
+ if (changed) {
+ rebuildBlackFrameLocked();
}
- setForcedDisplaySizeLocked(width, height);
}
- private void setForcedDisplaySizeLocked(int width, int height) {
+ private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) {
Slog.i(TAG, "Using new display size: " + width + "x" + height);
- synchronized(mDisplaySizeLock) {
- mBaseDisplayWidth = width;
- mBaseDisplayHeight = height;
+ synchronized(displayContent.mDisplaySizeLock) {
+ displayContent.mBaseDisplayWidth = width;
+ displayContent.mBaseDisplayHeight = height;
}
- mPolicy.setInitialDisplaySize(mDisplay, mBaseDisplayWidth, mBaseDisplayHeight);
+ reconfigureDisplayLocked(displayContent);
+ }
+
+ public void clearForcedDisplaySize(int displayId) {
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
+ displayContent.mInitialDisplayHeight);
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.DISPLAY_SIZE_FORCED, "");
+ }
+ }
+
+ public void setForcedDisplayDensity(int displayId, int density) {
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ setForcedDisplayDensityLocked(displayContent, density);
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.DISPLAY_DENSITY_FORCED, Integer.toString(density));
+ }
+ }
+
+ private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) {
+ Slog.i(TAG, "Using new display density: " + density);
+
+ synchronized(displayContent.mDisplaySizeLock) {
+ displayContent.mBaseDisplayDensity = density;
+ }
+ reconfigureDisplayLocked(displayContent);
+ }
+
+ public void clearForcedDisplayDensity(int displayId) {
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContent(displayId);
+ setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity);
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.DISPLAY_DENSITY_FORCED, "");
+ }
+ }
+
+ private void reconfigureDisplayLocked(DisplayContent displayContent) {
+ mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mBaseDisplayWidth,
+ displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity);
mLayoutNeeded = true;
@@ -7605,19 +7674,11 @@ public class WindowManagerService extends IWindowManager.Stub
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
- rebuildBlackFrame();
+ rebuildBlackFrameLocked();
performLayoutAndPlaceSurfacesLocked();
}
- public void clearForcedDisplaySize() {
- synchronized(mWindowMap) {
- setForcedDisplaySizeLocked(mInitialDisplayWidth, mInitialDisplayHeight);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DISPLAY_SIZE_FORCED, "");
- }
- }
-
public boolean hasSystemNavBar() {
return mPolicy.hasSystemNavBar();
}
@@ -7660,9 +7721,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
final void rebuildAppWindowListLocked() {
- int NW = mWindows.size();
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ rebuildAppWindowListLocked(iterator.next());
+ }
+ }
+
+ private void rebuildAppWindowListLocked(final DisplayContent displayContent) {
+ final WindowList windows = displayContent.getWindowList();
+ int NW = windows.size();
int i;
- int lastWallpaper = -1;
+ int lastBelow = -1;
int numRemoved = 0;
if (mRebuildTmp.length < NW) {
@@ -7672,9 +7741,9 @@ public class WindowManagerService extends IWindowManager.Stub
// First remove all existing app windows.
i=0;
while (i < NW) {
- WindowState w = mWindows.get(i);
+ WindowState w = windows.get(i);
if (w.mAppToken != null) {
- WindowState win = mWindows.remove(i);
+ WindowState win = windows.remove(i);
win.mRebuilding = true;
mRebuildTmp[numRemoved] = win;
mWindowsChanged = true;
@@ -7683,17 +7752,19 @@ public class WindowManagerService extends IWindowManager.Stub
NW--;
numRemoved++;
continue;
- } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER
- && lastWallpaper == i-1) {
- lastWallpaper = i;
+ } else if (lastBelow == i-1) {
+ if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER
+ || w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
+ lastBelow = i;
+ }
}
i++;
}
- // The wallpaper window(s) typically live at the bottom of the stack,
- // so skip them before adding app tokens.
- lastWallpaper++;
- i = lastWallpaper;
+ // Keep whatever windows were below the app windows still below,
+ // by skipping them.
+ lastBelow++;
+ i = lastBelow;
// First add all of the exiting app tokens... these are no longer
// in the main app list, but still have windows shown. We put them
@@ -7701,16 +7772,16 @@ public class WindowManagerService extends IWindowManager.Stub
// will care about them.
int NT = mExitingAppTokens.size();
for (int j=0; j<NT; j++) {
- i = reAddAppWindowsLocked(i, mExitingAppTokens.get(j));
+ i = reAddAppWindowsLocked(displayContent, i, mExitingAppTokens.get(j));
}
// And add in the still active app tokens in Z order.
NT = mAnimatingAppTokens.size();
for (int j=0; j<NT; j++) {
- i = reAddAppWindowsLocked(i, mAnimatingAppTokens.get(j));
+ i = reAddAppWindowsLocked(displayContent, i, mAnimatingAppTokens.get(j));
}
- i -= lastWallpaper;
+ i -= lastBelow;
if (i != numRemoved) {
Slog.w(TAG, "Rebuild removed " + numRemoved
+ " windows but added " + i);
@@ -7733,8 +7804,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- private final void assignLayersLocked() {
- int N = mWindows.size();
+ private final void assignLayersLocked(WindowList windows) {
+ int N = windows.size();
int curBaseLayer = 0;
int curLayer = 0;
int i;
@@ -7746,7 +7817,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
for (i=0; i<N; i++) {
- final WindowState w = mWindows.get(i);
+ final WindowState w = windows.get(i);
final WindowStateAnimator winAnimator = w.mWinAnimator;
boolean layerChanged = false;
int oldLayer = w.mLayer;
@@ -7781,7 +7852,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (layerChanged && mAnimator.isDimming(winAnimator)) {
// Force an animation pass just to update the mDimAnimator layer.
- scheduleAnimationLocked();
+ updateLayoutToAnimationLocked();
}
if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": "
+ winAnimator.mAnimLayer);
@@ -7808,7 +7879,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (mDisplay == null) {
+ if (mDefaultDisplay == null) {
// Not yet initialized, nothing to do.
return;
}
@@ -7839,34 +7910,32 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception while force removing for memory", e);
}
-
+
try {
- performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ performLayoutAndPlaceSurfacesLockedInner(displayContent, recoveringMemory);
+
+ final int N = mPendingRemove.size();
+ if (N > 0) {
+ if (mPendingRemoveTmp.length < N) {
+ mPendingRemoveTmp = new WindowState[N+10];
+ }
+ mPendingRemove.toArray(mPendingRemoveTmp);
+ mPendingRemove.clear();
+ for (int i=0; i<N; i++) {
+ WindowState w = mPendingRemoveTmp[i];
+ removeWindowInnerLocked(w.mSession, w);
+ }
- final int N = mPendingRemove.size();
- if (N > 0) {
- if (mPendingRemoveTmp.length < N) {
- mPendingRemoveTmp = new WindowState[N+10];
- }
- mPendingRemove.toArray(mPendingRemoveTmp);
- mPendingRemove.clear();
- for (int i=0; i<N; i++) {
- WindowState w = mPendingRemoveTmp[i];
- removeWindowInnerLocked(w.mSession, w);
+ assignLayersLocked(displayContent.getWindowList());
+ mLayoutNeeded = true;
}
-
- mInLayout = false;
- assignLayersLocked();
- mLayoutNeeded = true;
- // XXX this recursion seems broken!
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- performLayoutAndPlaceSurfacesLocked();
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
-
- } else {
- mInLayout = false;
}
+ mInLayout = false;
+
if (mLayoutNeeded) {
if (++mLayoutRepeatCount < 6) {
requestTraversalLocked();
@@ -7890,22 +7959,24 @@ public class WindowManagerService extends IWindowManager.Stub
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
- private final void performLayoutLockedInner(boolean initial, boolean updateInputWindows) {
+ private final void performLayoutLockedInner(final DisplayContent displayContent,
+ boolean initial, boolean updateInputWindows) {
if (!mLayoutNeeded) {
return;
}
-
+ WindowList windows = displayContent.getWindowList();
mLayoutNeeded = false;
-
- final int dw = mCurDisplayWidth;
- final int dh = mCurDisplayHeight;
+
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
final int NFW = mFakeWindows.size();
for (int i=0; i<NFW; i++) {
mFakeWindows.get(i).layout(dw, dh);
}
- final int N = mWindows.size();
+ final int N = windows.size();
int i;
if (DEBUG_LAYOUT) {
@@ -7913,9 +7984,12 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.v(TAG, "performLayout: needed="
+ mLayoutNeeded + " dw=" + dw + " dh=" + dh);
}
-
+
+ WindowStateAnimator universeBackground = null;
+
mPolicy.beginLayoutLw(dw, dh, mRotation);
mSystemDecorLayer = mPolicy.getSystemDecorRectLw(mSystemDecorRect);
+ mScreenRect.set(0, 0, dw, dh);
int seq = mLayoutSeq+1;
if (seq < 0) seq = 0;
@@ -7925,7 +7999,7 @@ public class WindowManagerService extends IWindowManager.Stub
// to another window).
int topAttached = -1;
for (i = N-1; i >= 0; i--) {
- final WindowState win = mWindows.get(i);
+ final WindowState win = windows.get(i);
// Don't do layout of a window if it is not visible, or
// soon won't be visible, to avoid wasting time and funky
@@ -7956,7 +8030,8 @@ public class WindowManagerService extends IWindowManager.Stub
// if they want. (We do the normal layout for INVISIBLE
// windows, since that means "perform layout as normal,
// just don't display").
- if (!gone || !win.mHaveFrame || win.mLayoutNeeded) {
+ if (!gone || !win.mHaveFrame || win.mLayoutNeeded
+ || win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) {
if (!win.mLayoutAttached) {
if (initial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
@@ -7974,6 +8049,16 @@ public class WindowManagerService extends IWindowManager.Stub
if (topAttached < 0) topAttached = i;
}
}
+ if (win.mViewVisibility == View.VISIBLE
+ && win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND
+ && universeBackground == null) {
+ universeBackground = win.mWinAnimator;
+ }
+ }
+
+ if (mAnimator.mUniverseBackground != universeBackground) {
+ mFocusMayChange = true;
+ mAnimator.mUniverseBackground = universeBackground;
}
// Now perform layout of attached windows, which usually
@@ -7981,7 +8066,7 @@ public class WindowManagerService extends IWindowManager.Stub
// XXX does not deal with windows that are attached to windows
// that are themselves attached.
for (i = topAttached; i >= 0; i--) {
- final WindowState win = mWindows.get(i);
+ final WindowState win = windows.get(i);
if (win.mLayoutAttached) {
if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win
@@ -8042,10 +8127,11 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+ * @param windows TODO(cmautner):
*
* @return bitmap indicating if another pass through layout must be made.
*/
- public int handleAppTransitionReadyLocked() {
+ public int handleAppTransitionReadyLocked(WindowList windows) {
int changes = 0;
int i;
int NN = mOpeningApps.size();
@@ -8200,15 +8286,23 @@ public class WindowManagerService extends IWindowManager.Stub
NN = mOpeningApps.size();
for (i=0; i<NN; i++) {
AppWindowToken wtoken = mOpeningApps.get(i);
+ final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
- wtoken.mAppAnimator.clearThumbnail();
+ appAnimator.clearThumbnail();
wtoken.reportedVisible = false;
wtoken.inPendingTransaction = false;
- wtoken.mAppAnimator.animation = null;
+ appAnimator.animation = null;
setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToShow = false;
- mAnimator.mAnimating |= wtoken.mAppAnimator.showAllWindowsLocked();
+
+ appAnimator.mAllAppWinAnimators.clear();
+ final int N = wtoken.allAppWindows.size();
+ for (int j = 0; j < N; j++) {
+ appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
+ }
+ mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+
if (animLp != null) {
int layer = -1;
for (int j=0; j<wtoken.windows.size(); j++) {
@@ -8249,7 +8343,8 @@ public class WindowManagerService extends IWindowManager.Stub
mNextAppTransitionThumbnail.getHeight());
try {
Surface surface = new Surface(mFxSession, Process.myPid(),
- "thumbnail anim", 0, dirty.width(), dirty.height(),
+ "thumbnail anim", mDefaultDisplay.getLayerStack(),
+ dirty.width(), dirty.height(),
PixelFormat.TRANSLUCENT, Surface.HIDDEN);
topOpeningApp.mAppAnimator.thumbnail = surface;
if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL "
@@ -8262,7 +8357,7 @@ public class WindowManagerService extends IWindowManager.Stub
drawSurface.release();
topOpeningApp.mAppAnimator.thumbnailLayer = topOpeningLayer;
Animation anim = createThumbnailAnimationLocked(
- transit, true, true, mNextAppTransitionDelayed);
+ transit, true, true, mNextAppTransitionScaleUp);
topOpeningApp.mAppAnimator.thumbnailAnimation = anim;
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mTransitionAnimationScale);
@@ -8289,11 +8384,12 @@ public class WindowManagerService extends IWindowManager.Stub
changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT
| WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
mLayoutNeeded = true;
- if (!moveInputMethodWindowsIfNeededLocked(true)) {
- assignLayersLocked();
+
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ if (windows == getDefaultWindowList() && !moveInputMethodWindowsIfNeededLocked(true)) {
+ assignLayersLocked(windows);
}
- updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
- false /*updateInputWindows*/);
+ updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, false /*updateInputWindows*/);
mFocusMayChange = false;
}
@@ -8302,7 +8398,6 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- *
* @return bitmap indicating if another pass through layout must be made.
*/
private int handleAnimatingStoppedAndTransitionLocked() {
@@ -8476,14 +8571,14 @@ public class WindowManagerService extends IWindowManager.Stub
if (!mAnimator.isDimming(winAnimator)) {
final int width, height;
if (attrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
- width = mCurDisplayWidth;
- height = mCurDisplayHeight;
+ final DisplayInfo displayInfo = w.mDisplayContent.getDisplayInfo();
+ width = displayInfo.logicalWidth;
+ height = displayInfo.logicalHeight;
} else {
width = innerDw;
height = innerDh;
}
- mAnimator.startDimming(winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount,
- width, height);
+ startDimming(winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount, width, height);
}
}
}
@@ -8511,21 +8606,23 @@ public class WindowManagerService extends IWindowManager.Stub
// "Something has changed! Let's make it correct now."
private final void performLayoutAndPlaceSurfacesLockedInner(
- boolean recoveringMemory) {
+ final DisplayContent displayContent, boolean recoveringMemory) {
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by "
+ Debug.getCallers(3));
}
- if (mDisplay == null) {
+ if (mDefaultDisplay == null) {
Slog.i(TAG, "skipping performLayoutAndPlaceSurfacesLockedInner with no mDisplay");
return;
}
+ final WindowList windows = displayContent.getWindowList();
final long currentTime = SystemClock.uptimeMillis();
- final int dw = mCurDisplayWidth;
- final int dh = mCurDisplayHeight;
- final int innerDw = mAppDisplayWidth;
- final int innerDh = mAppDisplayHeight;
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+ final int innerDw = displayInfo.appWidth;
+ final int innerDh = displayInfo.appHeight;
int i;
@@ -8577,8 +8674,8 @@ public class WindowManagerService extends IWindowManager.Stub
mPendingLayoutChanges);
if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
- assignLayersLocked();
+ if ((adjustWallpaperWindowsLocked() & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+ assignLayersLocked(windows);
mLayoutNeeded = true;
}
}
@@ -8597,7 +8694,7 @@ public class WindowManagerService extends IWindowManager.Stub
// FIRST LOOP: Perform a layout, if needed.
if (repeats < 4) {
- performLayoutLockedInner(repeats == 1, false /*updateInputWindows*/);
+ performLayoutLockedInner(displayContent, repeats == 1, false /*updateInputWindows*/);
} else {
Slog.w(TAG, "Layout repeat skipped after too many iterations");
}
@@ -8608,8 +8705,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount,
mPendingLayoutChanges);
mPolicy.beginAnimationLw(dw, dh);
- for (i = mWindows.size() - 1; i >= 0; i--) {
- WindowState w = mWindows.get(i);
+ for (i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
if (w.mHasSurface) {
mPolicy.animatingWindowLw(w, w.mAttrs);
}
@@ -8627,9 +8724,9 @@ public class WindowManagerService extends IWindowManager.Stub
boolean focusDisplayed = false;
boolean updateAllDrawn = false;
- final int N = mWindows.size();
+ final int N = windows.size();
for (i=N-1; i>=0; i--) {
- WindowState w = mWindows.get(i);
+ WindowState w = windows.get(i);
final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
@@ -8660,6 +8757,10 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.setAnimation(a);
winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+ try {
+ w.mClient.moved(w.mFrame.left, w.mFrame.top);
+ } catch (RemoteException e) {
+ }
}
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
@@ -8676,7 +8777,7 @@ public class WindowManagerService extends IWindowManager.Stub
mInnerFields.mWallpaperMayChange = true;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats("updateWindowsAndWallpaperLocked 1",
+ debugLayoutRepeats("wallpaper and commitFinishDrawingLocked true",
mPendingLayoutChanges);
}
}
@@ -8690,7 +8791,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ w.isOnScreen() + " allDrawn=" + atoken.allDrawn
+ " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
}
- if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
+ if (atoken != null
+ && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
if (atoken.lastTransactionSequence != mTransactionSequence) {
atoken.lastTransactionSequence = mTransactionSequence;
atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
@@ -8748,7 +8850,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (!mInnerFields.mDimming && mAnimator.isDimming()) {
- mAnimator.stopDimming();
+ stopDimming();
}
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
@@ -8760,7 +8862,7 @@ public class WindowManagerService extends IWindowManager.Stub
// all of the app tokens to be shown and see if they are ready
// to go.
if (mAppTransitionReady) {
- mPendingLayoutChanges |= handleAppTransitionReadyLocked();
+ mPendingLayoutChanges |= handleAppTransitionReadyLocked(windows);
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
mPendingLayoutChanges);
}
@@ -8803,7 +8905,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper layer changed: assigning layers + relayout");
mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- assignLayersLocked();
+ assignLayersLocked(windows);
} else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper visibility changed: relayout");
@@ -8847,9 +8949,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ORIENTATION &&
winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i(
TAG, "Resizing " + win + " WITH DRAW PENDING");
- win.mClient.resized((int)winAnimator.mSurfaceW,
- (int)winAnimator.mSurfaceH,
- win.mLastContentInsets, win.mLastVisibleInsets,
+ win.mClient.resized(win.mFrame, win.mLastContentInsets, win.mLastVisibleInsets,
winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING,
configChanged ? win.mConfiguration : null);
win.mContentInsetsChanged = false;
@@ -8899,6 +8999,7 @@ public class WindowManagerService extends IWindowManager.Stub
mExitingTokens.remove(i);
if (token.windowType == TYPE_WALLPAPER) {
mWallpaperTokens.remove(token);
+ updateLayoutToAnimWallpaperTokens();
}
}
}
@@ -8940,31 +9041,25 @@ public class WindowManagerService extends IWindowManager.Stub
// Finally update all input windows now that the window changes have stabilized.
mInputMonitor.updateInputWindowsLw(true /*force*/);
- setHoldScreenLocked(mInnerFields.mHoldScreen != null);
+ setHoldScreenLocked(mInnerFields.mHoldScreen);
if (!mDisplayFrozen) {
if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) {
- mPowerManager.setScreenBrightnessOverride(-1);
+ mPowerManager.setScreenBrightnessOverrideFromWindowManager(-1);
} else {
- mPowerManager.setScreenBrightnessOverride((int)
- (mInnerFields.mScreenBrightness * PowerManager.BRIGHTNESS_ON));
+ mPowerManager.setScreenBrightnessOverrideFromWindowManager(
+ toBrightnessOverride(mInnerFields.mScreenBrightness));
}
if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) {
- mPowerManager.setButtonBrightnessOverride(-1);
+ mPowerManager.setButtonBrightnessOverrideFromWindowManager(-1);
} else {
- mPowerManager.setButtonBrightnessOverride((int)
- (mInnerFields.mButtonBrightness * PowerManager.BRIGHTNESS_ON));
+ mPowerManager.setButtonBrightnessOverrideFromWindowManager(
+ toBrightnessOverride(mInnerFields.mButtonBrightness));
}
}
- if (mInnerFields.mHoldScreen != mHoldingScreenOn) {
- mHoldingScreenOn = mInnerFields.mHoldScreen;
- Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, mInnerFields.mHoldScreen);
- mH.sendMessage(m);
- }
if (mTurnOnScreen) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!");
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
- LocalPowerManager.BUTTON_EVENT, true);
+ mPowerManager.wakeUp(SystemClock.uptimeMillis());
mTurnOnScreen = false;
}
@@ -8986,7 +9081,7 @@ public class WindowManagerService extends IWindowManager.Stub
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
- scheduleAnimationLocked();
+ updateLayoutToAnimationLocked();
if (DEBUG_WINDOW_TRACE) {
Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges="
@@ -8995,6 +9090,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ private int toBrightnessOverride(float value) {
+ return (int)(value * PowerManager.BRIGHTNESS_ON);
+ }
+
void checkDrawnWindowsLocked() {
if (mWaitingForDrawn.size() > 0) {
for (int j=mWaitingForDrawn.size()-1; j>=0; j--) {
@@ -9040,13 +9139,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- /**
- * Must be called with the main window manager lock held.
- */
- void setHoldScreenLocked(boolean holding) {
- boolean state = mHoldingScreenWakeLock.isHeld();
- if (holding != state) {
- if (holding) {
+ void setHoldScreenLocked(final Session newHoldScreen) {
+ final boolean hold = newHoldScreen != null;
+
+ if (hold && mHoldingScreenOn != newHoldScreen) {
+ mHoldingScreenWakeLock.setWorkSource(new WorkSource(newHoldScreen.mUid));
+ }
+ mHoldingScreenOn = newHoldScreen;
+
+ final boolean state = mHoldingScreenWakeLock.isHeld();
+ if (hold != state) {
+ if (hold) {
mPolicy.screenOnStartedLw();
mHoldingScreenWakeLock.acquire();
} else {
@@ -9063,13 +9166,118 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ /** Note that Locked in this case is on mLayoutToAnim */
void scheduleAnimationLocked() {
- if (!mAnimationScheduled) {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationRunnable, null);
- mAnimationScheduled = true;
+ final LayoutToAnimatorParams layoutToAnim = mLayoutToAnim;
+ if (!layoutToAnim.mAnimationScheduled) {
+ layoutToAnim.mAnimationScheduled = true;
+ mChoreographer.postCallback(
+ Choreographer.CALLBACK_ANIMATION, mAnimator.mAnimationRunnable, null);
+ }
+ }
+
+ void updateLayoutToAnimationLocked() {
+ final LayoutToAnimatorParams layoutToAnim = mLayoutToAnim;
+ synchronized (layoutToAnim) {
+ // Copy local params to transfer params.
+ ArrayList<WinAnimatorList> allWinAnimatorLists = layoutToAnim.mWinAnimatorLists;
+ allWinAnimatorLists.clear();
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ WinAnimatorList winAnimatorList = new WinAnimatorList();
+ final WindowList windows = displayContent.getWindowList();
+ int N = windows.size();
+ for (int i = 0; i < N; i++) {
+ final WindowStateAnimator winAnimator = windows.get(i).mWinAnimator;
+ if (winAnimator.mSurface != null) {
+ winAnimatorList.add(winAnimator);
+ }
+ }
+ allWinAnimatorLists.add(winAnimatorList);
+ }
+
+ layoutToAnim.mWallpaperTarget = mWallpaperTarget;
+ layoutToAnim.mLowerWallpaperTarget = mLowerWallpaperTarget;
+ layoutToAnim.mUpperWallpaperTarget = mUpperWallpaperTarget;
+
+ final ArrayList<AppWindowAnimParams> paramList = layoutToAnim.mAppWindowAnimParams;
+ paramList.clear();
+ int N = mAnimatingAppTokens.size();
+ for (int i = 0; i < N; i++) {
+ paramList.add(new AppWindowAnimParams(mAnimatingAppTokens.get(i).mAppAnimator));
+ }
+
+ layoutToAnim.mParamsModified = true;
+ scheduleAnimationLocked();
}
}
+ void updateLayoutToAnimWallpaperTokens() {
+ synchronized(mLayoutToAnim) {
+ mLayoutToAnim.mWallpaperTokens = new ArrayList<WindowToken>(mWallpaperTokens);
+ mLayoutToAnim.mChanges |= LayoutToAnimatorParams.WALLPAPER_TOKENS_CHANGED;
+ }
+ }
+
+ void setAnimDimParams(DimAnimator.Parameters params) {
+ synchronized (mLayoutToAnim) {
+ mLayoutToAnim.mDimParams = params;
+ scheduleAnimationLocked();
+ }
+ }
+
+ void startDimming(final WindowStateAnimator winAnimator, final float target,
+ final int width, final int height) {
+ setAnimDimParams(new DimAnimator.Parameters(winAnimator, width, height, target));
+ }
+
+ void stopDimming() {
+ setAnimDimParams(null);
+ }
+
+ private boolean copyAnimToLayoutParamsLocked() {
+ boolean doRequest = false;
+ final WindowAnimator.AnimatorToLayoutParams animToLayout = mAnimator.mAnimToLayout;
+ synchronized (animToLayout) {
+ animToLayout.mUpdateQueued = false;
+ final int bulkUpdateParams = animToLayout.mBulkUpdateParams;
+ // TODO(cmautner): As the number of bits grows, use masks of bit groups to
+ // eliminate unnecessary tests.
+ if ((bulkUpdateParams & LayoutFields.SET_UPDATE_ROTATION) != 0) {
+ mInnerFields.mUpdateRotation = true;
+ doRequest = true;
+ }
+ if ((bulkUpdateParams & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) {
+ mInnerFields.mWallpaperMayChange = true;
+ doRequest = true;
+ }
+ if ((bulkUpdateParams & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) {
+ mInnerFields.mWallpaperForceHidingChanged = true;
+ doRequest = true;
+ }
+ if ((bulkUpdateParams & LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
+ mInnerFields.mOrientationChangeComplete = false;
+ } else {
+ mInnerFields.mOrientationChangeComplete = true;
+ if (mWindowsFreezingScreen) {
+ doRequest = true;
+ }
+ }
+ if ((bulkUpdateParams & LayoutFields.SET_TURN_ON_SCREEN) != 0) {
+ mTurnOnScreen = true;
+ }
+
+ mPendingLayoutChanges |= animToLayout.mPendingLayoutChanges;
+ if (mPendingLayoutChanges != 0) {
+ doRequest = true;
+ }
+
+ mWindowDetachedWallpaper = animToLayout.mWindowDetachedWallpaper;
+ }
+ return doRequest;
+ }
+
boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation,
boolean secure) {
final Surface surface = winAnimator.mSurface;
@@ -9088,10 +9296,11 @@ public class WindowManagerService extends IWindowManager.Stub
// There was some problem... first, do a sanity check of the
// window list to make sure we haven't left any dangling surfaces
// around.
- int N = mWindows.size();
+
+ AllWindowsIterator iterator = new AllWindowsIterator();
Slog.i(TAG, "Out of memory for surface! Looking for leaks...");
- for (int i=0; i<N; i++) {
- WindowState ws = mWindows.get(i);
+ while (iterator.hasNext()) {
+ WindowState ws = iterator.next();
WindowStateAnimator wsa = ws.mWinAnimator;
if (wsa.mSurface != null) {
if (!mSessions.contains(wsa.mSession)) {
@@ -9106,8 +9315,6 @@ public class WindowManagerService extends IWindowManager.Stub
wsa.mSurface = null;
ws.mHasSurface = false;
mForceRemoves.add(ws);
- i--;
- N--;
leakedSurface = true;
} else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
@@ -9126,8 +9333,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (!leakedSurface) {
Slog.w(TAG, "No leaked surfaces; killing applicatons!");
SparseIntArray pidCandidates = new SparseIntArray();
- for (int i=0; i<N; i++) {
- WindowStateAnimator wsa = mWindows.get(i).mWinAnimator;
+ iterator = new AllWindowsIterator();
+ while (iterator.hasNext()) {
+ WindowState ws = iterator.next();
+ if (mForceRemoves.contains(ws)) {
+ continue;
+ }
+ WindowStateAnimator wsa = ws.mWinAnimator;
if (wsa.mSurface != null) {
pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
}
@@ -9187,6 +9399,9 @@ public class WindowManagerService extends IWindowManager.Stub
mLosingFocus.remove(newFocus);
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
+ // TODO(multidisplay): Focused windows on default display only.
+ final DisplayContent displayContent = getDefaultDisplayContent();
+
final WindowState imWindow = mInputMethodWindow;
if (newFocus != imWindow && oldFocus != imWindow) {
if (moveInputMethodWindowsIfNeededLocked(
@@ -9195,12 +9410,12 @@ public class WindowManagerService extends IWindowManager.Stub
mLayoutNeeded = true;
}
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- performLayoutLockedInner(true /*initial*/, updateInputWindows);
+ performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
- assignLayersLocked();
+ assignLayersLocked(displayContent.getWindowList());
}
}
@@ -9208,7 +9423,7 @@ public class WindowManagerService extends IWindowManager.Stub
// The change in focus caused us to need to do a layout. Okay.
mLayoutNeeded = true;
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- performLayoutLockedInner(true /*initial*/, updateInputWindows);
+ performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
}
}
@@ -9232,12 +9447,19 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState result = null;
WindowState win;
+ if (mAnimator.mUniverseBackground != null
+ && mAnimator.mUniverseBackground.mWin.canReceiveKeys()) {
+ return mAnimator.mUniverseBackground.mWin;
+ }
+
int nextAppIndex = mAppTokens.size()-1;
WindowToken nextApp = nextAppIndex >= 0
? mAppTokens.get(nextAppIndex) : null;
- for (int i = mWindows.size() - 1; i >= 0; i--) {
- win = mWindows.get(i);
+ // TODO(multidisplay): IMEs are only supported on the default display.
+ WindowList windows = getDefaultWindowList();
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ win = windows.get(i);
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Looking for focus: " + i
@@ -9300,7 +9522,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (mDisplay == null || !mPolicy.isScreenOnFully()) {
+ if (mDefaultDisplay == null || !mPolicy.isScreenOnFully()) {
// No need to freeze the screen before the system is ready or if
// the screen is off.
return;
@@ -9331,15 +9553,11 @@ public class WindowManagerService extends IWindowManager.Stub
mAnimator.mScreenRotationAnimation = null;
}
+ // TODO(multidisplay): rotation on main screen only.
+ DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo();
mAnimator.mScreenRotationAnimation = new ScreenRotationAnimation(mContext,
- mFxSession, inTransaction, mCurDisplayWidth, mCurDisplayHeight,
- mDisplay.getRotation());
-
- if (!mAnimator.mScreenRotationAnimation.hasScreenshot()) {
- Surface.freezeDisplay(0);
- }
- } else {
- Surface.freezeDisplay(0);
+ mDefaultDisplay, mFxSession, inTransaction, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, mDefaultDisplay.getRotation());
}
}
@@ -9363,13 +9581,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean updateRotation = false;
-
+
if (CUSTOM_SCREEN_ROTATION && mAnimator.mScreenRotationAnimation != null
&& mAnimator.mScreenRotationAnimation.hasScreenshot()) {
if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation");
+ // TODO(multidisplay): rotation on main screen only.
+ DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo();
if (mAnimator.mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
- mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) {
- scheduleAnimationLocked();
+ mTransitionAnimationScale, displayInfo.logicalWidth,
+ displayInfo.logicalHeight)) {
+ updateLayoutToAnimationLocked();
} else {
mAnimator.mScreenRotationAnimation.kill();
mAnimator.mScreenRotationAnimation = null;
@@ -9382,7 +9603,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
updateRotation = true;
}
- Surface.unfreezeDisplay(0);
mInputMonitor.thawInputDispatchingLw();
@@ -9448,7 +9668,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (line != null) {
String[] toks = line.split("%");
if (toks != null && toks.length > 0) {
- mWatermark = new Watermark(mRealDisplayMetrics, mFxSession, toks);
+ mWatermark =
+ new Watermark(mDefaultDisplay, mRealDisplayMetrics, mFxSession, toks);
}
}
} catch (FileNotFoundException e) {
@@ -9478,11 +9699,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ // TOOD(multidisplay): StatusBar on multiple screens?
void updateStatusBarVisibilityLocked(int visibility) {
mInputManager.setSystemUiVisibility(visibility);
- final int N = mWindows.size();
+ final WindowList windows = getDefaultWindowList();
+ final int N = windows.size();
for (int i = 0; i < N; i++) {
- WindowState ws = mWindows.get(i);
+ WindowState ws = windows.get(i);
try {
int curValue = ws.mSystemUiVisibility;
int diff = curValue ^ visibility;
@@ -9549,6 +9772,8 @@ public class WindowManagerService extends IWindowManager.Stub
// It is assumed that this method is called only by InputMethodManagerService.
public void saveLastInputMethodWindowForTransition() {
synchronized (mWindowMap) {
+ // TODO(multidisplay): Pass in the displayID.
+ DisplayContent displayContent = getDefaultDisplayContent();
if (mInputMethodWindow != null) {
mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
}
@@ -9699,10 +9924,12 @@ public class WindowManagerService extends IWindowManager.Stub
void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
- for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = mWindows.get(i);
+ int j = 0;
+ final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
+ while (iterator.hasNext()) {
+ final WindowState w = iterator.next();
if (windows == null || windows.contains(w)) {
- pw.print(" Window #"); pw.print(i); pw.print(' ');
+ pw.print(" Window #"); pw.print(j++); pw.print(' ');
pw.print(w); pw.println(":");
w.dump(pw, " ", dumpAll || windows != null);
}
@@ -9810,27 +10037,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
pw.println();
- if (mDisplay != null) {
- pw.print(" Display: init="); pw.print(mInitialDisplayWidth); pw.print("x");
- pw.print(mInitialDisplayHeight);
- if (mInitialDisplayWidth != mBaseDisplayWidth
- || mInitialDisplayHeight != mBaseDisplayHeight) {
- pw.print(" base=");
- pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
- }
- final int rawWidth = mDisplay.getRawWidth();
- final int rawHeight = mDisplay.getRawHeight();
- if (rawWidth != mCurDisplayWidth || rawHeight != mCurDisplayHeight) {
- pw.print(" raw="); pw.print(rawWidth); pw.print("x"); pw.print(rawHeight);
- }
- pw.print(" cur=");
- pw.print(mCurDisplayWidth); pw.print("x"); pw.print(mCurDisplayHeight);
- pw.print(" app=");
- pw.print(mAppDisplayWidth); pw.print("x"); pw.print(mAppDisplayHeight);
- pw.print(" rng="); pw.print(mSmallestDisplayWidth);
- pw.print("x"); pw.print(mSmallestDisplayHeight);
- pw.print("-"); pw.print(mLargestDisplayWidth);
- pw.print("x"); pw.println(mLargestDisplayHeight);
+ if (mDefaultDisplay != null) {
+ DisplayContentsIterator dCIterator = new DisplayContentsIterator();
+ while (dCIterator.hasNext()) {
+ dCIterator.next().dump(pw);
+ }
} else {
pw.println(" NO DISPLAY");
}
@@ -9847,7 +10058,8 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
if (dumpAll) {
pw.print(" mSystemDecorRect="); pw.print(mSystemDecorRect.toShortString());
- pw.print(" mSystemDecorLayer="); pw.println(mSystemDecorLayer);
+ pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer);
+ pw.print(" mScreenRecr="); pw.println(mScreenRect.toShortString());
if (mLastStatusBarVisibility != 0) {
pw.print(" mLastStatusBarVisibility=0x");
pw.println(Integer.toHexString(mLastStatusBarVisibility));
@@ -9916,15 +10128,15 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mNextAppTransitionStartHeight=");
pw.println(mNextAppTransitionStartHeight);
break;
- case ActivityOptions.ANIM_THUMBNAIL:
- case ActivityOptions.ANIM_THUMBNAIL_DELAYED:
+ case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP:
+ case ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN:
pw.print(" mNextAppTransitionThumbnail=");
pw.print(mNextAppTransitionThumbnail);
pw.print(" mNextAppTransitionStartX=");
pw.print(mNextAppTransitionStartX);
pw.print(" mNextAppTransitionStartY=");
pw.println(mNextAppTransitionStartY);
- pw.print(" mNextAppTransitionDelayed="); pw.println(mNextAppTransitionDelayed);
+ pw.print(" mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp);
break;
}
if (mNextAppTransitionCallback != null) {
@@ -9943,8 +10155,9 @@ public class WindowManagerService extends IWindowManager.Stub
ArrayList<WindowState> windows = new ArrayList<WindowState>();
if ("visible".equals(name)) {
synchronized(mWindowMap) {
- for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = mWindows.get(i);
+ final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
+ while (iterator.hasNext()) {
+ final WindowState w = iterator.next();
if (w.mWinAnimator.mSurfaceShown) {
windows.add(w);
}
@@ -9959,8 +10172,9 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RuntimeException e) {
}
synchronized(mWindowMap) {
- for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = mWindows.get(i);
+ final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
+ while (iterator.hasNext()) {
+ final WindowState w = iterator.next();
if (name != null) {
if (w.mAttrs.getTitle().toString().contains(name)) {
windows.add(w);
@@ -10133,7 +10347,6 @@ public class WindowManagerService extends IWindowManager.Stub
// Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
public void monitor() {
synchronized (mWindowMap) { }
- synchronized (mKeyguardTokenWatcher) { }
}
public interface OnHardKeyboardStatusChangeListener {
@@ -10147,8 +10360,108 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void bulkSetParameters(final int bulkUpdateParams, int pendingLayoutChanges) {
- mH.sendMessage(mH.obtainMessage(H.BULK_UPDATE_PARAMETERS, bulkUpdateParams,
- pendingLayoutChanges));
+ public DisplayContent getDisplayContent(final int displayId) {
+ DisplayContent displayContent = mDisplayContents.get(displayId);
+ if (displayContent == null) {
+ displayContent = new DisplayContent(mDisplayManager.getRealDisplay(displayId));
+ mDisplayContents.put(displayId, displayContent);
+ }
+ return displayContent;
+ }
+
+ class DisplayContentsIterator implements Iterator<DisplayContent> {
+ private int cur;
+
+ @Override
+ public boolean hasNext() {
+ return cur < mDisplayContents.size();
+ }
+
+ @Override
+ public DisplayContent next() {
+ if (hasNext()) {
+ return mDisplayContents.valueAt(cur++);
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new IllegalArgumentException("AllDisplayContentIterator.remove not implemented");
+ }
+ }
+
+ boolean REVERSE_ITERATOR = true;
+ class AllWindowsIterator implements Iterator<WindowState> {
+ private DisplayContent mDisplayContent;
+ private DisplayContentsIterator mDisplayContentsIterator;
+ private WindowList mWindowList;
+ private int mWindowListIndex;
+ private boolean mReverse;
+
+ AllWindowsIterator() {
+ mDisplayContentsIterator = new DisplayContentsIterator();
+ mDisplayContent = mDisplayContentsIterator.next();
+ mWindowList = mDisplayContent.getWindowList();
+ }
+
+ AllWindowsIterator(boolean reverse) {
+ this();
+ mReverse = reverse;
+ mWindowListIndex = reverse ? mWindowList.size() - 1 : 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (mReverse) {
+ return mWindowListIndex >= 0;
+ }
+ return mWindowListIndex < mWindowList.size();
+ }
+
+ @Override
+ public WindowState next() {
+ if (hasNext()) {
+ WindowState win = mWindowList.get(mWindowListIndex);
+ if (mReverse) {
+ mWindowListIndex--;
+ if (mWindowListIndex < 0 && mDisplayContentsIterator.hasNext()) {
+ mDisplayContent = mDisplayContentsIterator.next();
+ mWindowList = mDisplayContent.getWindowList();
+ mWindowListIndex = mWindowList.size() - 1;
+ }
+ } else {
+ mWindowListIndex++;
+ if (mWindowListIndex >= mWindowList.size()
+ && mDisplayContentsIterator.hasNext()) {
+ mDisplayContent = mDisplayContentsIterator.next();
+ mWindowList = mDisplayContent.getWindowList();
+ mWindowListIndex = 0;
+ }
+ }
+ return win;
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new IllegalArgumentException("AllWindowsIterator.remove not implemented");
+ }
}
+
+ public DisplayContent getDefaultDisplayContent() {
+ final int displayId = mDefaultDisplay == null
+ ? Display.DEFAULT_DISPLAY : mDefaultDisplay.getDisplayId();
+ return getDisplayContent(displayId);
+ }
+
+ public WindowList getDefaultWindowList() {
+ return getDefaultDisplayContent().getWindowList();
+ }
+
+ public DisplayInfo getDefaultDisplayInfo() {
+ return getDefaultDisplayContent().getDisplayInfo();
+ }
+
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index a00e8a5..a52e1d7 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -22,6 +22,9 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import com.android.server.input.InputWindowHandle;
@@ -33,8 +36,11 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Slog;
+import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IWindow;
@@ -47,6 +53,9 @@ import android.view.WindowManagerPolicy;
import java.io.PrintWriter;
import java.util.ArrayList;
+class WindowList extends ArrayList<WindowState> {
+}
+
/**
* A window in the window manager.
*/
@@ -251,15 +260,22 @@ final class WindowState implements WindowManagerPolicy.WindowState {
boolean mHasSurface = false;
+ DisplayContent mDisplayContent;
+
+ // UserId and appId of the owner. Don't display windows of non-current user.
+ final int mOwnerUid;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int seq, WindowManager.LayoutParams a,
- int viewVisibility) {
+ int viewVisibility, final DisplayContent displayContent) {
mService = service;
mSession = s;
mClient = c;
mToken = token;
+ mOwnerUid = s.mUid;
mAttrs.copyFrom(a);
mViewVisibility = viewVisibility;
+ mDisplayContent = displayContent;
mPolicy = mService.mPolicy;
mContext = mService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
@@ -317,9 +333,6 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mIsFloatingLayer = mIsImWindow || mIsWallpaper;
}
- mWinAnimator = new WindowStateAnimator(service, this, mAttachedWindow);
- mWinAnimator.mAlpha = a.alpha;
-
WindowState appWin = this;
while (appWin.mAttachedWindow != null) {
appWin = appWin.mAttachedWindow;
@@ -335,6 +348,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mRootToken = appToken;
mAppToken = appToken.appWindowToken;
+ mWinAnimator = new WindowStateAnimator(this);
+ mWinAnimator.mAlpha = a.alpha;
+
mRequestedWidth = 0;
mRequestedHeight = 0;
mLastRequestedWidth = 0;
@@ -343,7 +359,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mYOffset = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
- mAppToken != null ? mAppToken.mInputApplicationHandle : null, this);
+ mAppToken != null ? mAppToken.mInputApplicationHandle : null, this,
+ displayContent.getDisplayId());
}
void attach() {
@@ -479,8 +496,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
- mService.updateWallpaperOffsetLocked(this,
- mService.mAppDisplayWidth, mService.mAppDisplayHeight, false);
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+ mService.updateWallpaperOffsetLocked(this, displayInfo.appWidth, displayInfo.appHeight,
+ false);
}
if (WindowManagerService.localLOGV) {
@@ -544,6 +562,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
int index = -1;
WindowState ws = this;
+ WindowList windows = getWindowList();
while (true) {
if ((ws.mAttrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY) != 0) {
@@ -558,13 +577,13 @@ final class WindowState implements WindowManagerPolicy.WindowState {
// look behind it.
// First, we may need to determine the starting position.
if (index < 0) {
- index = mService.mWindows.indexOf(ws);
+ index = windows.indexOf(ws);
}
index--;
if (index < 0) {
return false;
}
- ws = mService.mWindows.get(index);
+ ws = windows.get(index);
}
}
@@ -884,6 +903,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
boolean showLw(boolean doAnimation, boolean requestAnim) {
+ if (isOtherUsersAppWindow()) {
+ Slog.w(TAG, "Current user " + mService.mCurrentUserId + " trying to display "
+ + this + ", type " + mAttrs.type + ", belonging to " + mOwnerUid);
+ return false;
+ }
if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
// Already showing.
return false;
@@ -907,7 +931,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_ENTER, true);
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mService.updateLayoutToAnimationLocked();
}
return true;
}
@@ -950,7 +974,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mService.updateLayoutToAnimationLocked();
}
return true;
}
@@ -960,6 +984,17 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mClient.asBinder().isBinderAlive();
}
+ boolean isOtherUsersAppWindow() {
+ final int type = mAttrs.type;
+ if ((UserHandle.getUserId(mOwnerUid) != mService.mCurrentUserId)
+ && (mOwnerUid != Process.SYSTEM_UID)
+ && (type >= TYPE_BASE_APPLICATION) && (type <= LAST_APPLICATION_WINDOW)
+ && (type != TYPE_APPLICATION_STARTING)) {
+ return true;
+ }
+ return false;
+ }
+
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
outRegion.set(
frame.left + inset.left, frame.top + inset.top,
@@ -988,8 +1023,13 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
}
+ WindowList getWindowList() {
+ return mDisplayContent.getWindowList();
+ }
+
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- pw.print(prefix); pw.print("mSession="); pw.print(mSession);
+ pw.print(prefix); pw.print("mDisplayId="); pw.print(mDisplayContent.getDisplayId());
+ pw.print(" mSession="); pw.print(mSession);
pw.print(" mClient="); pw.println(mClient.asBinder());
pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs);
pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
@@ -1060,6 +1100,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (mTouchableInsets != 0 || mGivenInsetsPending) {
pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets);
pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending);
+ Region region = new Region();
+ getTouchableRegion(region);
+ pw.print(prefix); pw.print("touchable region="); pw.println(region);
}
pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration);
}
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 03e52fe..982f60d 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -3,9 +3,8 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static com.android.server.wm.WindowManagerService.LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN;
import android.content.Context;
@@ -17,6 +16,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.os.Debug;
import android.util.Slog;
+import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -31,6 +31,9 @@ import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.ArrayList;
+class WinAnimatorList extends ArrayList<WindowStateAnimator> {
+}
+
/**
* Keep track of animations and surface operations for a single WindowState.
**/
@@ -48,13 +51,20 @@ class WindowStateAnimator {
static final String TAG = "WindowStateAnimator";
+ // Unchanging local convenience fields.
final WindowManagerService mService;
final WindowState mWin;
- final WindowState mAttachedWindow;
+ final WindowStateAnimator mAttachedWinAnimator;
final WindowAnimator mAnimator;
+ final AppWindowAnimator mAppAnimator;
final Session mSession;
final WindowManagerPolicy mPolicy;
final Context mContext;
+ final boolean mIsWallpaper;
+
+ // If this is a universe background window, this is the transformation
+ // it is applying to the rest of the universe.
+ final Transformation mUniverseTransform = new Transformation();
// Currently running animation.
boolean mAnimating;
@@ -138,19 +148,28 @@ class WindowStateAnimator {
int mAttrFlags;
int mAttrType;
- public WindowStateAnimator(final WindowManagerService service, final WindowState win,
- final WindowState attachedWindow) {
+ final int mLayerStack;
+
+ public WindowStateAnimator(final WindowState win) {
+ final WindowManagerService service = win.mService;
+
mService = service;
+ mAnimator = service.mAnimator;
+ mPolicy = service.mPolicy;
+ mContext = service.mContext;
+ final DisplayInfo displayInfo = win.mDisplayContent.getDisplayInfo();
+ mAnimDw = displayInfo.appWidth;
+ mAnimDh = displayInfo.appHeight;
+
mWin = win;
- mAttachedWindow = attachedWindow;
- mAnimator = mService.mAnimator;
+ mAttachedWinAnimator = win.mAttachedWindow == null
+ ? null : win.mAttachedWindow.mWinAnimator;
+ mAppAnimator = win.mAppToken == null ? null : win.mAppToken.mAppAnimator;
mSession = win.mSession;
- mPolicy = mService.mPolicy;
- mContext = mService.mContext;
mAttrFlags = win.mAttrs.flags;
mAttrType = win.mAttrs.type;
- mAnimDw = service.mAppDisplayWidth;
- mAnimDh = service.mAppDisplayHeight;
+ mIsWallpaper = win.mIsWallpaper;
+ mLayerStack = win.mDisplayContent.getDisplay().getLayerStack();
}
public void setAnimation(Animation anim) {
@@ -177,20 +196,17 @@ class WindowStateAnimator {
/** Is the window or its container currently animating? */
boolean isAnimating() {
- final WindowState attached = mAttachedWindow;
- final AppWindowToken atoken = mWin.mAppToken;
return mAnimation != null
- || (attached != null && attached.mWinAnimator.mAnimation != null)
- || (atoken != null &&
- (atoken.mAppAnimator.animation != null
- || atoken.inPendingTransaction));
+ || (mAttachedWinAnimator != null && mAttachedWinAnimator.mAnimation != null)
+ || (mAppAnimator != null &&
+ (mAppAnimator.animation != null
+ || mAppAnimator.mAppToken.inPendingTransaction));
}
/** Is the window animating the DummyAnimation? */
boolean isDummyAnimation() {
- final AppWindowToken atoken = mWin.mAppToken;
- return atoken != null
- && atoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
+ return mAppAnimator != null
+ && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
}
/** Is this window currently animating? */
@@ -213,7 +229,7 @@ class WindowStateAnimator {
}
mTransformation.clear();
final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
- if (DEBUG_ANIM) Slog.v(
+ if (false && DEBUG_ANIM) Slog.v(
TAG, "Stepped animation in " + this +
": more=" + more + ", xform=" + mTransformation);
return more;
@@ -240,8 +256,9 @@ class WindowStateAnimator {
" scale=" + mService.mWindowAnimationScale);
mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
mAnimDw, mAnimDh);
- mAnimDw = mService.mAppDisplayWidth;
- mAnimDh = mService.mAppDisplayHeight;
+ final DisplayInfo displayInfo = mWin.mDisplayContent.getDisplayInfo();
+ mAnimDw = displayInfo.appWidth;
+ mAnimDh = displayInfo.appHeight;
mAnimation.setStartTime(currentTime);
mLocalAnimating = true;
mAnimating = true;
@@ -257,8 +274,8 @@ class WindowStateAnimator {
//WindowManagerService.this.dump();
}
mHasLocalTransformation = false;
- if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null
- && mWin.mAppToken.mAppAnimator.animation != null) {
+ if ((!mLocalAnimating || mAnimationIsEntrance) && mAppAnimator != null
+ && mAppAnimator.animation != null) {
// When our app token is animating, we kind-of pretend like
// we are as well. Note the mLocalAnimating mAnimationIsEntrance
// part of this check means that we will only do this if
@@ -305,7 +322,7 @@ class WindowStateAnimator {
mAnimLayer = mWin.mLayer;
if (mWin.mIsImWindow) {
mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
- } else if (mWin.mIsWallpaper) {
+ } else if (mIsWallpaper) {
mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this
@@ -460,22 +477,14 @@ class WindowStateAnimator {
private final Point mSize = new Point();
private final Rect mWindowCrop = new Rect();
private boolean mShown = false;
- private String mName = "Not named";
-
- public SurfaceTrace(SurfaceSession s,
- int pid, int display, int w, int h, int format, int flags) throws
- OutOfResourcesException {
- super(s, pid, display, w, h, format, flags);
- mSize.set(w, h);
- Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
- + Debug.getCallers(3));
- }
+ private int mLayerStack;
+ private String mName;
public SurfaceTrace(SurfaceSession s,
- int pid, String name, int display, int w, int h, int format, int flags)
+ int pid, String name, int layerStack, int w, int h, int format, int flags)
throws OutOfResourcesException {
- super(s, pid, name, display, w, h, format, flags);
- mName = name;
+ super(s, pid, name, layerStack, w, h, format, flags);
+ mName = name != null ? name : "Not named";
mSize.set(w, h);
Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
+ Debug.getCallers(3));
@@ -534,6 +543,13 @@ class WindowStateAnimator {
}
@Override
+ public void setLayerStack(int layerStack) {
+ super.setLayerStack(layerStack);
+ mLayerStack = layerStack;
+ Slog.v(SURFACE_TAG, "setLayerStack: " + this + ". Called by " + Debug.getCallers(3));
+ }
+
+ @Override
public void hide() {
super.hide();
mShown = false;
@@ -574,7 +590,7 @@ class WindowStateAnimator {
@Override
public String toString() {
return "Surface " + Integer.toHexString(System.identityHashCode(this)) + " "
- + mName + ": shown=" + mShown + " layer=" + mLayer
+ + mName + " (" + mLayerStack + "): shown=" + mShown + " layer=" + mLayer
+ " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y
+ " " + mSize.x + "x" + mSize.y
+ " crop=" + mWindowCrop.toShortString();
@@ -638,12 +654,12 @@ class WindowStateAnimator {
mSurface = new SurfaceTrace(
mSession.mSurfaceSession, mSession.mPid,
attrs.getTitle().toString(),
- 0, w, h, format, flags);
+ mLayerStack, w, h, format, flags);
} else {
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
attrs.getTitle().toString(),
- 0, w, h, format, flags);
+ mLayerStack, w, h, format, flags);
}
mWin.mHasSurface = true;
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
@@ -689,10 +705,6 @@ class WindowStateAnimator {
mSurface.setAlpha(0);
mSurfaceShown = false;
mSurface.hide();
- if ((mWin.mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "DITHER", null);
- mSurface.setFlags(Surface.SURFACE_DITHER, Surface.SURFACE_DITHER);
- }
} catch (RuntimeException e) {
Slog.w(TAG, "Error creating surface in " + w, e);
mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
@@ -799,31 +811,28 @@ class WindowStateAnimator {
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;
Transformation attachedTransformation =
- (mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation)
- ? mAttachedWindow.mWinAnimator.mTransformation : null;
- final AppWindowAnimator appAnimator =
- mWin.mAppToken == null ? null : mWin.mAppToken.mAppAnimator;
- Transformation appTransformation = (appAnimator != null && appAnimator.hasTransformation)
- ? appAnimator.transformation : null;
+ (mAttachedWinAnimator != null && mAttachedWinAnimator.mHasLocalTransformation)
+ ? mAttachedWinAnimator.mTransformation : null;
+ Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation)
+ ? mAppAnimator.transformation : null;
// Wallpapers are animated based on the "real" window they
// are currently targeting.
- if (mWin.mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null
- && mService.mWallpaperTarget != null) {
- if (mService.mWallpaperTarget.mWinAnimator.mHasLocalTransformation &&
- mService.mWallpaperTarget.mWinAnimator.mAnimation != null &&
- !mService.mWallpaperTarget.mWinAnimator.mAnimation.getDetachWallpaper()) {
- attachedTransformation = mService.mWallpaperTarget.mWinAnimator.mTransformation;
+ if (mIsWallpaper && mAnimator.mLowerWallpaperTarget == null
+ && mAnimator.mWallpaperTarget != null) {
+ final WindowStateAnimator wallpaperAnimator = mAnimator.mWallpaperTarget.mWinAnimator;
+ if (wallpaperAnimator.mHasLocalTransformation &&
+ wallpaperAnimator.mAnimation != null &&
+ !wallpaperAnimator.mAnimation.getDetachWallpaper()) {
+ attachedTransformation = wallpaperAnimator.mTransformation;
if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) {
Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
}
}
- final AppWindowAnimator wpAppAnimator = mService.mWallpaperTarget.mAppToken == null
- ? null : mService.mWallpaperTarget.mAppToken.mAppAnimator;
- if (wpAppAnimator != null &&
- wpAppAnimator.hasTransformation &&
- wpAppAnimator.animation != null &&
- !wpAppAnimator.animation.getDetachWallpaper()) {
+ final AppWindowAnimator wpAppAnimator = mAnimator.mWpAppAnimator;
+ if (wpAppAnimator != null && wpAppAnimator.hasTransformation
+ && wpAppAnimator.animation != null
+ && !wpAppAnimator.animation.getDetachWallpaper()) {
appTransformation = wpAppAnimator.transformation;
if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
Slog.v(TAG, "WP target app xform: " + appTransformation);
@@ -870,6 +879,9 @@ class WindowStateAnimator {
if (appTransformation != null) {
tmpMatrix.postConcat(appTransformation.getMatrix());
}
+ if (mAnimator.mUniverseBackground != null) {
+ tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
+ }
if (screenAnimation) {
tmpMatrix.postConcat(
mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getMatrix());
@@ -913,6 +925,9 @@ class WindowStateAnimator {
if (appTransformation != null) {
mShownAlpha *= appTransformation.getAlpha();
}
+ if (mAnimator.mUniverseBackground != null) {
+ mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
+ }
if (screenAnimation) {
mShownAlpha *=
mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getAlpha();
@@ -921,7 +936,7 @@ class WindowStateAnimator {
//Slog.i(TAG, "Not applying alpha transform");
}
- if (WindowManagerService.localLOGV && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
+ if ((DEBUG_SURFACE_TRACE || WindowManagerService.localLOGV) && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
TAG, "computeShownFrameLocked: Animating " + this +
" mAlpha=" + mAlpha +
" self=" + (selfTransformation ? mTransformation.getAlpha() : "null") +
@@ -930,7 +945,7 @@ class WindowStateAnimator {
" screen=" + (screenAnimation ? mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getAlpha()
: "null"));
return;
- } else if (mWin.mIsWallpaper &&
+ } else if (mIsWallpaper &&
(mAnimator.mPendingActions & WindowAnimator.WALLPAPER_ACTION_PENDING) != 0) {
return;
}
@@ -938,16 +953,64 @@ class WindowStateAnimator {
if (WindowManagerService.localLOGV) Slog.v(
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- mWin.mShownFrame.set(mWin.mFrame);
- if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
- mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset);
- }
- mShownAlpha = mAlpha;
- mHaveMatrix = false;
- mDsDx = mWin.mGlobalScale;
- mDtDx = 0;
- mDsDy = 0;
- mDtDy = mWin.mGlobalScale;
+ if (mAnimator.mUniverseBackground != null &&
+ mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
+ && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer) {
+ final Rect frame = mWin.mFrame;
+ final float tmpFloats[] = mService.mTmpFloats;
+ final Matrix tmpMatrix = mWin.mTmpMatrix;
+ tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
+ tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
+ tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
+ tmpMatrix.getValues(tmpFloats);
+ mHaveMatrix = true;
+ mDsDx = tmpFloats[Matrix.MSCALE_X];
+ mDtDx = tmpFloats[Matrix.MSKEW_Y];
+ mDsDy = tmpFloats[Matrix.MSKEW_X];
+ mDtDy = tmpFloats[Matrix.MSCALE_Y];
+ float x = tmpFloats[Matrix.MTRANS_X];
+ float y = tmpFloats[Matrix.MTRANS_Y];
+ int w = frame.width();
+ int h = frame.height();
+ mWin.mShownFrame.set(x, y, x+w, y+h);
+ mShownAlpha = mAlpha * mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
+ } else {
+ mWin.mShownFrame.set(mWin.mFrame);
+ if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+ mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset);
+ }
+ mShownAlpha = mAlpha;
+ mHaveMatrix = false;
+ mDsDx = mWin.mGlobalScale;
+ mDtDx = 0;
+ mDsDy = 0;
+ mDtDy = mWin.mGlobalScale;
+ }
+ }
+
+ void applyDecorRect(final Rect decorRect) {
+ final WindowState w = mWin;
+ // Compute the offset of the window in relation to the decor rect.
+ final int offX = w.mXOffset + w.mFrame.left;
+ final int offY = w.mYOffset + w.mFrame.top;
+ // Initialize the decor rect to the entire frame.
+ w.mSystemDecorRect.set(0, 0, w.mFrame.width(), w.mFrame.height());
+ // Intersect with the decor rect, offsetted by window position.
+ w.mSystemDecorRect.intersect(decorRect.left-offX, decorRect.top-offY,
+ decorRect.right-offX, decorRect.bottom-offY);
+ // If size compatibility is being applied to the window, the
+ // surface is scaled relative to the screen. Also apply this
+ // scaling to the crop rect. We aren't using the standard rect
+ // scale function because we want to round things to make the crop
+ // always round to a larger rect to ensure we don't crop too
+ // much and hide part of the window that should be seen.
+ if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) {
+ final float scale = w.mInvGlobalScale;
+ w.mSystemDecorRect.left = (int) (w.mSystemDecorRect.left * scale - 0.5f);
+ w.mSystemDecorRect.top = (int) (w.mSystemDecorRect.top * scale - 0.5f);
+ w.mSystemDecorRect.right = (int) ((w.mSystemDecorRect.right+1) * scale - 0.5f);
+ w.mSystemDecorRect.bottom = (int) ((w.mSystemDecorRect.bottom+1) * scale - 0.5f);
+ }
}
void updateSurfaceWindowCrop(final boolean recoveringMemory) {
@@ -960,31 +1023,21 @@ class WindowStateAnimator {
w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
} else if (w.mLayer >= mService.mSystemDecorLayer) {
// Above the decor layer is easy, just use the entire window.
+ // Unless we have a universe background... in which case all the
+ // windows need to be cropped by the screen, so they don't cover
+ // the universe background.
+ if (mAnimator.mUniverseBackground == null) {
+ w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
+ w.mCompatFrame.height());
+ } else {
+ applyDecorRect(mService.mScreenRect);
+ }
+ } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
+ // The universe background isn't cropped.
w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
w.mCompatFrame.height());
} else {
- final Rect decorRect = mService.mSystemDecorRect;
- // Compute the offset of the window in relation to the decor rect.
- final int offX = w.mXOffset + w.mFrame.left;
- final int offY = w.mYOffset + w.mFrame.top;
- // Initialize the decor rect to the entire frame.
- w.mSystemDecorRect.set(0, 0, w.mFrame.width(), w.mFrame.height());
- // Intersect with the decor rect, offsetted by window position.
- w.mSystemDecorRect.intersect(decorRect.left-offX, decorRect.top-offY,
- decorRect.right-offX, decorRect.bottom-offY);
- // If size compatibility is being applied to the window, the
- // surface is scaled relative to the screen. Also apply this
- // scaling to the crop rect. We aren't using the standard rect
- // scale function because we want to round things to make the crop
- // always round to a larger rect to ensure we don't crop too
- // much and hide part of the window that should be seen.
- if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) {
- final float scale = w.mInvGlobalScale;
- w.mSystemDecorRect.left = (int) (w.mSystemDecorRect.left * scale - 0.5f);
- w.mSystemDecorRect.top = (int) (w.mSystemDecorRect.top * scale - 0.5f);
- w.mSystemDecorRect.right = (int) ((w.mSystemDecorRect.right+1) * scale - 0.5f);
- w.mSystemDecorRect.bottom = (int) ((w.mSystemDecorRect.bottom+1) * scale - 0.5f);
- }
+ applyDecorRect(mService.mSystemDecorRect);
}
if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) {
@@ -1056,8 +1109,9 @@ class WindowStateAnimator {
mAnimator.mPendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
- mAnimator.startDimming(this, w.mExiting ? 0 : w.mAttrs.dimAmount,
- mService.mAppDisplayWidth, mService.mAppDisplayHeight);
+ final DisplayInfo displayInfo = mWin.mDisplayContent.getDisplayInfo();
+ mService.startDimming(this, w.mExiting ? 0 : w.mAttrs.dimAmount,
+ displayInfo.appWidth, displayInfo.appHeight);
}
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
@@ -1092,7 +1146,7 @@ class WindowStateAnimator {
setSurfaceBoundaries(recoveringMemory);
- if (mWin.mIsWallpaper && !mWin.mWallpaperVisible) {
+ if (mIsWallpaper && !mWin.mWallpaperVisible) {
// Wallpaper is no longer visible and there is no wp target => hide it.
hide();
} else if (w.mAttachedHidden || !w.isReadyForDisplay()) {
@@ -1151,7 +1205,7 @@ class WindowStateAnimator {
+ " during relayout");
if (showSurfaceRobustlyLocked()) {
mLastHidden = false;
- if (w.mIsWallpaper) {
+ if (mIsWallpaper) {
mService.dispatchWallpaperVisibility(w, true);
}
} else {
@@ -1169,8 +1223,8 @@ class WindowStateAnimator {
}
}
} else {
- if (DEBUG_ANIM) {
- // Slog.v(TAG, "prepareSurface: No changes in animation for " + mWin);
+ if (DEBUG_ANIM && isAnimating()) {
+ Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
}
displayed = true;
}
@@ -1178,7 +1232,7 @@ class WindowStateAnimator {
if (displayed) {
if (w.mOrientationChanging) {
if (!w.isDrawnLw()) {
- mAnimator.mBulkUpdateParams |= CLEAR_ORIENTATION_CHANGE_COMPLETE;
+ mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation continue waiting for draw in " + w);
} else {
@@ -1239,6 +1293,11 @@ class WindowStateAnimator {
// This must be called while inside a transaction.
boolean performShowLocked() {
+ if (mWin.isOtherUsersAppWindow()) {
+ Slog.w(TAG, "Current user " + mService.mCurrentUserId + " trying to display "
+ + this + ", type " + mWin.mAttrs.type + ", belonging to " + mWin.mOwnerUid);
+ return false;
+ }
if (DEBUG_VISIBILITY || (DEBUG_STARTING_WINDOW &&
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING)) {
RuntimeException e = null;
@@ -1258,7 +1317,7 @@ class WindowStateAnimator {
+ (mWin.mAppToken != null ? mWin.mAppToken.hidden : false)
+ " animating=" + mAnimating
+ " tok animating="
- + (mWin.mAppToken != null ? mWin.mAppToken.mAppAnimator.animating : false), e);
+ + (mAppAnimator != null ? mAppAnimator.animating : false), e);
}
if (mDrawState == READY_TO_SHOW && mWin.isReadyForDisplayIgnoringKeyguard()) {
if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
@@ -1274,7 +1333,7 @@ class WindowStateAnimator {
+ (mWin.mAppToken != null ? mWin.mAppToken.hidden : false)
+ " animating=" + mAnimating
+ " tok animating="
- + (mWin.mAppToken != null ? mWin.mAppToken.mAppAnimator.animating : false));
+ + (mAppAnimator != null ? mAppAnimator.animating : false));
}
mService.enableScreenIfNeededLocked();
@@ -1286,7 +1345,7 @@ class WindowStateAnimator {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM)
Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + this);
mDrawState = HAS_DRAWN;
- mService.scheduleAnimationLocked();
+ mService.updateLayoutToAnimationLocked();
int i = mWin.mChildWindows.size();
while (i > 0) {
@@ -1483,6 +1542,11 @@ class WindowStateAnimator {
pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
}
+ if (mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
+ pw.print(prefix); pw.print("mUniverseTransform=");
+ mUniverseTransform.printShortString(pw);
+ pw.println();
+ }
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
pw.print(" mAlpha="); pw.print(mAlpha);