diff options
Diffstat (limited to 'services/java/com/android')
20 files changed, 887 insertions, 448 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 9f01eca..f241c80 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -1474,6 +1474,7 @@ class BackupManagerService extends IBackupManager.Stub { if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName); removeEverBackedUp(packageName); set.remove(packageName); + mPendingBackups.remove(packageName); } } @@ -1625,6 +1626,7 @@ class BackupManagerService extends IBackupManager.Stub { } catch (InterruptedException e) { // just bail if (DEBUG) Slog.w(TAG, "Interrupted: " + e); + mActivityManager.clearPendingBackup(); return null; } } @@ -1632,6 +1634,7 @@ class BackupManagerService extends IBackupManager.Stub { // if we timed out with no connect, abort and move on if (mConnecting == true) { Slog.w(TAG, "Timeout waiting for agent " + app); + mActivityManager.clearPendingBackup(); return null; } if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 63eeeb3..37dee19 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -23,8 +23,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +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.content.res.Resources; import android.database.ContentObserver; import android.location.Address; @@ -90,10 +93,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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; - private static final String ACCESS_COARSE_LOCATION = - android.Manifest.permission.ACCESS_COARSE_LOCATION; + // Location resolution level: no location data whatsoever + private static final int RESOLUTION_LEVEL_NONE = 0; + // Location resolution level: coarse location data only + private static final int RESOLUTION_LEVEL_COARSE = 1; + // Location resolution level: fine location data + private static final int RESOLUTION_LEVEL_FINE = 2; + private static final String ACCESS_MOCK_LOCATION = android.Manifest.permission.ACCESS_MOCK_LOCATION; private static final String ACCESS_LOCATION_EXTRA_COMMANDS = @@ -246,6 +252,74 @@ public class LocationManagerService extends ILocationManager.Stub implements Run updateProvidersLocked(); } + private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) { + PackageManager pm = mContext.getPackageManager(); + String systemPackageName = mContext.getPackageName(); + ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs); + + List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser( + new Intent(FUSED_LOCATION_SERVICE_ACTION), + PackageManager.GET_META_DATA, mCurrentUserId); + for (ResolveInfo rInfo : rInfos) { + String packageName = rInfo.serviceInfo.packageName; + + // Check that the signature is in the list of supported sigs. If it's not in + // this list the standard provider binding logic won't bind to it. + try { + PackageInfo pInfo; + pInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + if (!ServiceWatcher.isSignatureMatch(pInfo.signatures, sigSets)) { + Log.w(TAG, packageName + " resolves service " + FUSED_LOCATION_SERVICE_ACTION + + ", but has wrong signature, ignoring"); + continue; + } + } catch (NameNotFoundException e) { + Log.e(TAG, "missing package: " + packageName); + continue; + } + + // Get the version info + if (rInfo.serviceInfo.metaData == null) { + Log.w(TAG, "Found fused provider without metadata: " + packageName); + continue; + } + + int version = rInfo.serviceInfo.metaData.getInt( + ServiceWatcher.EXTRA_SERVICE_VERSION, -1); + if (version == 0) { + // This should be the fallback fused location provider. + + // Make sure it's in the system partition. + if ((rInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + if (D) Log.d(TAG, "Fallback candidate not in /system: " + packageName); + continue; + } + + // Check that the fallback is signed the same as the OS + // as a proxy for coreApp="true" + if (pm.checkSignatures(systemPackageName, packageName) + != PackageManager.SIGNATURE_MATCH) { + if (D) Log.d(TAG, "Fallback candidate not signed the same as system: " + + packageName); + continue; + } + + // Found a valid fallback. + if (D) Log.d(TAG, "Found fallback provider: " + packageName); + return; + } else { + if (D) Log.d(TAG, "Fallback candidate not version 0: " + packageName); + } + } + + throw new IllegalStateException("Unable to find a fused location provider that is in the " + + "system partition with version 0 and signed with the platform certificate. " + + "Such a package is needed to provide a default fused location provider in the " + + "event that no other fused location provider has been installed or is currently " + + "available. For example, coreOnly boot mode when decrypting the data " + + "partition. The fallback must also be marked coreApp=\"true\" in the manifest"); + } + private void loadProvidersLocked() { // create a passive location provider, which is always enabled PassiveProvider passiveProvider = new PassiveProvider(this); @@ -275,14 +349,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run */ Resources resources = mContext.getResources(); ArrayList<String> providerPackageNames = new ArrayList<String>(); - String[] pkgs1 = resources.getStringArray( + String[] pkgs = 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)); + if (D) Log.d(TAG, "certificates for location providers pulled from: " + + Arrays.toString(pkgs)); + if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs)); + + ensureFallbackFusedProviderPresentLocked(providerPackageNames); // bind to network provider LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind( @@ -347,7 +420,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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 int mAllowedResolutionLevel; // resolution level allowed to receiver final ILocationListener mListener; final PendingIntent mPendingIntent; @@ -366,7 +439,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } else { mKey = intent; } - mPermission = checkPermission(); + mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid); mUid = uid; mPid = pid; mPackageName = packageName; @@ -440,7 +513,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler, - mPermission); + getResolutionPermission(mAllowedResolutionLevel)); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -474,7 +547,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, - mPermission); + getResolutionPermission(mAllowedResolutionLevel)); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -512,7 +585,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler, - mPermission); + getResolutionPermission(mAllowedResolutionLevel)); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -609,51 +682,76 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } /** - * Returns the best permission available to the caller. + * Returns the permission string associated with the specified resolution level. + * + * @param resolutionLevel the resolution level + * @return the permission string */ - private String getBestCallingPermission() { - 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; + private String getResolutionPermission(int resolutionLevel) { + switch (resolutionLevel) { + case RESOLUTION_LEVEL_FINE: + return android.Manifest.permission.ACCESS_FINE_LOCATION; + case RESOLUTION_LEVEL_COARSE: + return android.Manifest.permission.ACCESS_COARSE_LOCATION; + default: + return null; } - return null; } /** - * Throw SecurityException if caller has neither COARSE or FINE. - * Otherwise, return the best permission. + * Returns the resolution level allowed to the given PID/UID pair. + * + * @param pid the PID + * @param uid the UID + * @return resolution level allowed to the pid/uid pair */ - private String checkPermission() { - String perm = getBestCallingPermission(); - if (perm == null) { - throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" + - " ACCESS_FINE_LOCATION permission"); + private int getAllowedResolutionLevel(int pid, int uid) { + if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, + pid, uid) == PackageManager.PERMISSION_GRANTED) { + return RESOLUTION_LEVEL_FINE; + } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, + pid, uid) == PackageManager.PERMISSION_GRANTED) { + return RESOLUTION_LEVEL_COARSE; + } else { + return RESOLUTION_LEVEL_NONE; } - return perm; } /** - * Throw SecurityException if caller lacks permission to use Geofences. + * Returns the resolution level allowed to the caller + * + * @return resolution level allowed to caller + */ + private int getCallerAllowedResolutionLevel() { + return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid()); + } + + /** + * Throw SecurityException if specified resolution level is insufficient to use geofences. + * + * @param allowedResolutionLevel resolution level allowed to caller */ - private void checkGeofencePermission() { - if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != - PackageManager.PERMISSION_GRANTED) { + private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) { + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission"); } } - private String getMinimumPermissionForProvider(String provider) { + /** + * Return the minimum resolution level required to use the specified location provider. + * + * @param provider the name of the location provider + * @return minimum resolution level required for provider + */ + private int getMinimumResolutionLevelForProviderUse(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) || LocationManager.PASSIVE_PROVIDER.equals(provider)) { // gps and passive providers require FINE permission - return ACCESS_FINE_LOCATION; + return RESOLUTION_LEVEL_FINE; } else if (LocationManager.NETWORK_PROVIDER.equals(provider) || LocationManager.FUSED_PROVIDER.equals(provider)) { // network and fused providers are ok with COARSE or FINE - return ACCESS_COARSE_LOCATION; + return RESOLUTION_LEVEL_COARSE; } else { // mock providers LocationProviderInterface lp = mMockProviders.get(provider); @@ -662,41 +760,38 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (properties != null) { if (properties.mRequiresSatellite) { // provider requiring satellites require FINE permission - return ACCESS_FINE_LOCATION; + return RESOLUTION_LEVEL_FINE; } else if (properties.mRequiresNetwork || properties.mRequiresCell) { // provider requiring network and or cell require COARSE or FINE - return ACCESS_COARSE_LOCATION; + return RESOLUTION_LEVEL_COARSE; } } } } - - return null; + return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE } - private boolean isPermissionSufficient(String perm, String minPerm) { - if (ACCESS_FINE_LOCATION.equals(minPerm)) { - return ACCESS_FINE_LOCATION.equals(perm); - } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) { - return ACCESS_FINE_LOCATION.equals(perm) || - ACCESS_COARSE_LOCATION.equals(perm); - } else { - return false; - } - } - - private void checkPermissionForProvider(String perm, String provider) { - String minPerm = getMinimumPermissionForProvider(provider); - if (!isPermissionSufficient(perm, minPerm)) { - if (ACCESS_FINE_LOCATION.equals(minPerm)) { - throw new SecurityException("Location provider \"" + provider + - "\" requires ACCESS_FINE_LOCATION permission."); - } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) { - throw new SecurityException("Location provider \"" + provider + - "\" requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission."); - } else { - throw new SecurityException("Insufficient permission for location provider \"" + - provider + "\"."); + /** + * Throw SecurityException if specified resolution level is insufficient to use the named + * location provider. + * + * @param allowedResolutionLevel resolution level allowed to caller + * @param providerName the name of the location provider + */ + private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel, + String providerName) { + int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName); + if (allowedResolutionLevel < requiredResolutionLevel) { + switch (requiredResolutionLevel) { + case RESOLUTION_LEVEL_FINE: + throw new SecurityException("\"" + providerName + "\" location provider " + + "requires ACCESS_FINE_LOCATION permission."); + case RESOLUTION_LEVEL_COARSE: + throw new SecurityException("\"" + providerName + "\" location provider " + + "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission."); + default: + throw new SecurityException("Insufficient permission for \"" + providerName + + "\" location provider."); } } } @@ -731,8 +826,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run */ @Override public List<String> getProviders(Criteria criteria, boolean enabledOnly) { + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); ArrayList<String> out; - String perm = getBestCallingPermission(); int callingUserId = UserHandle.getCallingUserId(); long identity = Binder.clearCallingIdentity(); try { @@ -743,7 +838,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (LocationManager.FUSED_PROVIDER.equals(name)) { continue; } - if (isPermissionSufficient(perm, getMinimumPermissionForProvider(name))) { + if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) { if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) { continue; } @@ -803,8 +898,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public boolean providerMeetsCriteria(String provider, Criteria criteria) { - checkPermission(); - LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { throw new IllegalArgumentException("provider=" + provider); @@ -1010,33 +1103,41 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return receiver; } - private String checkPermissionAndRequest(LocationRequest request) { - String perm = getBestCallingPermission(); - String provider = request.getProvider(); - checkPermissionForProvider(perm, provider); - - if (ACCESS_COARSE_LOCATION.equals(perm)) { - switch (request.getQuality()) { + /** + * Creates a LocationRequest based upon the supplied LocationRequest that to meets resolution + * and consistency requirements. + * + * @param request the LocationRequest from which to create a sanitized version + * @param shouldBeCoarse whether the sanitized version should be held to coarse resolution + * constraints + * @param fastestCoarseIntervalMS minimum interval allowed for coarse resolution + * @return a version of request that meets the given resolution and consistency requirements + * @hide + */ + private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel) { + LocationRequest sanitizedRequest = new LocationRequest(request); + if (resolutionLevel < RESOLUTION_LEVEL_FINE) { + switch (sanitizedRequest.getQuality()) { case LocationRequest.ACCURACY_FINE: - request.setQuality(LocationRequest.ACCURACY_BLOCK); + sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK); break; case LocationRequest.POWER_HIGH: - request.setQuality(LocationRequest.POWER_LOW); + sanitizedRequest.setQuality(LocationRequest.POWER_LOW); break; } // throttle - if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) { - request.setInterval(LocationFudger.FASTEST_INTERVAL_MS); + if (sanitizedRequest.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) { + sanitizedRequest.setInterval(LocationFudger.FASTEST_INTERVAL_MS); } - if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) { - request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS); + if (sanitizedRequest.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) { + sanitizedRequest.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS); } } // make getFastestInterval() the minimum of interval and fastest interval - if (request.getFastestInterval() > request.getInterval()) { + if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) { request.setFastestInterval(request.getInterval()); } - return perm; + return sanitizedRequest; } private void checkPackageName(String packageName) { @@ -1079,7 +1180,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run PendingIntent intent, String packageName) { if (request == null) request = DEFAULT_LOCATION_REQUEST; checkPackageName(packageName); - checkPermissionAndRequest(request); + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, + request.getProvider()); + LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -1089,7 +1193,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - requestLocationUpdatesLocked(request, recevier, pid, uid, packageName); + requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName); } } finally { Binder.restoreCallingIdentity(identity); @@ -1132,7 +1236,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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); @@ -1188,8 +1292,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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); + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkPackageName(packageName); + checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, + request.getProvider()); + // no need to sanitize this request, as only the provider name is used long identity = Binder.clearCallingIdentity(); try { @@ -1213,13 +1320,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (location == null) { return null; } - if (ACCESS_FINE_LOCATION.equals(perm)) { - return location; - } else { + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); if (noGPSLocation != null) { return mLocationFudger.getOrCreate(noGPSLocation); } + } else { + return location; } } return null; @@ -1232,18 +1339,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent, String packageName) { if (request == null) request = DEFAULT_LOCATION_REQUEST; - checkGeofencePermission(); - checkPermissionAndRequest(request); + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel); checkPendingIntent(intent); checkPackageName(packageName); + checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, + request.getProvider()); + LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel); - if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent); + if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + 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); + mGeofenceManager.addFence(sanitizedRequest, geofence, intent, uid, packageName); } finally { Binder.restoreCallingIdentity(identity); } @@ -1251,7 +1361,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) { - checkGeofencePermission(); + checkResolutionLevelIsSufficientForGeofenceUse(getCallerAllowedResolutionLevel()); checkPendingIntent(intent); checkPackageName(packageName); @@ -1272,10 +1382,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (mGpsStatusProvider == null) { return false; } - if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); - } + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + LocationManager.GPS_PROVIDER); try { mGpsStatusProvider.addGpsStatusListener(listener); @@ -1303,8 +1411,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // throw NullPointerException to remain compatible with previous implementation throw new NullPointerException(); } + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + provider); - checkPermission(); // and check for ACCESS_LOCATION_EXTRA_COMMANDS if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { @@ -1344,7 +1453,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return null; } - checkPermissionForProvider(getBestCallingPermission(), provider); + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + provider); LocationProviderInterface p; synchronized (mLock) { @@ -1357,7 +1467,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public boolean isProviderEnabled(String provider) { - checkPermissionForProvider(getBestCallingPermission(), provider); + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + provider); if (LocationManager.FUSED_PROVIDER.equals(provider)) return false; long identity = Binder.clearCallingIdentity(); @@ -1522,10 +1633,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } Location notifyLocation = null; - if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) { - notifyLocation = lastLocation; // use fine location + if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) { + notifyLocation = coarseLocation; // use coarse location } else { - notifyLocation = coarseLocation; // use coarse location if available + notifyLocation = lastLocation; // use fine location } if (notifyLocation != null) { Location lastLoc = r.mLastFixBroadcast; diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java index 5598b0a..2e7c6d1 100644 --- a/services/java/com/android/server/ServiceWatcher.java +++ b/services/java/com/android/server/ServiceWatcher.java @@ -43,7 +43,7 @@ import java.util.List; */ public class ServiceWatcher implements ServiceConnection { private static final boolean D = false; - private static final String EXTRA_VERSION = "version"; + public static final String EXTRA_SERVICE_VERSION = "serviceVersion"; private final String mTag; private final Context mContext; @@ -58,9 +58,27 @@ public class ServiceWatcher implements ServiceConnection { // all fields below synchronized on mLock private IBinder mBinder; // connected service private String mPackageName; // current best package - private int mVersion; // current best version + private int mVersion = Integer.MIN_VALUE; // current best version private int mCurrentUserId; + public static ArrayList<HashSet<Signature>> getSignatureSets(Context context, + List<String> initialPackageNames) { + PackageManager pm = context.getPackageManager(); + ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>(); + for (int i = 0, size = initialPackageNames.size(); i < size; i++) { + String pkg = initialPackageNames.get(i); + try { + HashSet<Signature> set = new HashSet<Signature>(); + Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures; + set.addAll(Arrays.asList(sigs)); + sigSets.add(set); + } catch (NameNotFoundException e) { + Log.w("ServiceWatcher", pkg + " not found"); + } + } + return sigSets; + } + public ServiceWatcher(Context context, String logTag, String action, List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) { mContext = context; @@ -71,20 +89,7 @@ public class ServiceWatcher implements ServiceConnection { mHandler = handler; mCurrentUserId = userId; - 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"); - } - } - + mSignatureSets = getSignatureSets(context, initialPackageNames); } public boolean start() { @@ -132,15 +137,16 @@ public class ServiceWatcher implements ServiceConnection { // check version int version = 0; if (rInfo.serviceInfo.metaData != null) { - version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0); + version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0); } + if (version > mVersion) { bestVersion = version; bestPackage = packageName; } } - if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s", + if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction, (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "), rInfos.size(), (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage))); @@ -174,7 +180,8 @@ public class ServiceWatcher implements ServiceConnection { | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId); } - private boolean isSignatureMatch(Signature[] signatures) { + public static boolean isSignatureMatch(Signature[] signatures, + List<HashSet<Signature>> sigSets) { if (signatures == null) return false; // build hashset of input to test against @@ -184,7 +191,7 @@ public class ServiceWatcher implements ServiceConnection { } // test input against each of the signature sets - for (HashSet<Signature> referenceSet : mSignatureSets) { + for (HashSet<Signature> referenceSet : sigSets) { if (referenceSet.equals(inputSet)) { return true; } @@ -192,6 +199,10 @@ public class ServiceWatcher implements ServiceConnection { return false; } + private boolean isSignatureMatch(Signature[] signatures) { + return isSignatureMatch(signatures, mSignatureSets); + } + private final PackageMonitor mPackageMonitor = new PackageMonitor() { /** * Called when package has been reinstalled diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java index caf37b7..0f04b44 100644 --- a/services/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java @@ -40,6 +40,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; +import android.text.TextUtils; import android.util.Property; import android.util.Slog; import android.view.Display; @@ -71,6 +72,7 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Locale; /** * This class handles the screen magnification when accessibility is enabled. @@ -1000,45 +1002,44 @@ public final class ScreenMagnifier implements EventStreamTransformation { mViewport.recomputeBounds(mMagnificationController.isMagnifying()); } break; } - } else { - switch (transition) { - case WindowManagerPolicy.TRANSIT_ENTER: - case WindowManagerPolicy.TRANSIT_SHOW: { - if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) { - break; - } - final int type = info.type; - switch (type) { - // TODO: Are these all the windows we want to make - // visible when they appear on the screen? - // Do we need to take some of them out? - case WindowManager.LayoutParams.TYPE_APPLICATION: - case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: - case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: - case WindowManager.LayoutParams.TYPE_SEARCH_BAR: - case WindowManager.LayoutParams.TYPE_PHONE: - case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: - case WindowManager.LayoutParams.TYPE_TOAST: - case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: - case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: - case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: - case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: - case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: - case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: - case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { - Rect magnifiedRegionBounds = mMagnificationController - .getMagnifiedRegionBounds(); - Rect touchableRegion = info.touchableRegion; - if (!magnifiedRegionBounds.intersect(touchableRegion)) { - ensureRectangleInMagnifiedRegionBounds( - magnifiedRegionBounds, touchableRegion); - } - } break; - } break; + } + switch (transition) { + case WindowManagerPolicy.TRANSIT_ENTER: + case WindowManagerPolicy.TRANSIT_SHOW: { + if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) { + break; } + final int type = info.type; + switch (type) { + // TODO: Are these all the windows we want to make + // visible when they appear on the screen? + // Do we need to take some of them out? + case WindowManager.LayoutParams.TYPE_APPLICATION: + case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: + case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: + case WindowManager.LayoutParams.TYPE_SEARCH_BAR: + case WindowManager.LayoutParams.TYPE_PHONE: + case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: + case WindowManager.LayoutParams.TYPE_TOAST: + case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: + case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: + case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: + case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: + case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: + case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { + Rect magnifiedRegionBounds = mMagnificationController + .getMagnifiedRegionBounds(); + Rect touchableRegion = info.touchableRegion; + if (!magnifiedRegionBounds.intersect(touchableRegion)) { + ensureRectangleInMagnifiedRegionBounds( + magnifiedRegionBounds, touchableRegion); + } + } break; + } break; } } } finally { @@ -1067,7 +1068,12 @@ public final class ScreenMagnifier implements EventStreamTransformation { final float scrollX; final float scrollY; if (rectangle.width() > magnifiedRegionBounds.width()) { - scrollX = rectangle.left - magnifiedRegionBounds.left; + final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); + if (direction == View.LAYOUT_DIRECTION_LTR) { + scrollX = rectangle.left - magnifiedRegionBounds.left; + } else { + scrollX = rectangle.right - magnifiedRegionBounds.right; + } } else if (rectangle.left < magnifiedRegionBounds.left) { scrollX = rectangle.left - magnifiedRegionBounds.left; } else if (rectangle.right > magnifiedRegionBounds.right) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 7132e1e..5722326 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -7251,11 +7251,11 @@ public final class ActivityManagerService extends ActivityManagerNative // care about. if (persistent) { final ContentResolver resolver = mContext.getContentResolver(); - Settings.System.putString( - resolver, Settings.System.DEBUG_APP, + Settings.Global.putString( + resolver, Settings.Global.DEBUG_APP, packageName); - Settings.System.putInt( - resolver, Settings.System.WAIT_FOR_DEBUGGER, + Settings.Global.putInt( + resolver, Settings.Global.WAIT_FOR_DEBUGGER, waitForDebugger ? 1 : 0); } @@ -7317,9 +7317,9 @@ public final class ActivityManagerService extends ActivityManagerNative enforceCallingPermission(android.Manifest.permission.SET_ALWAYS_FINISH, "setAlwaysFinish()"); - Settings.System.putInt( + Settings.Global.putInt( mContext.getContentResolver(), - Settings.System.ALWAYS_FINISH_ACTIVITIES, enabled ? 1 : 0); + Settings.Global.ALWAYS_FINISH_ACTIVITIES, enabled ? 1 : 0); synchronized (this) { mAlwaysFinishActivities = enabled; @@ -7596,12 +7596,12 @@ public final class ActivityManagerService extends ActivityManagerNative private void retrieveSettings() { final ContentResolver resolver = mContext.getContentResolver(); - String debugApp = Settings.System.getString( - resolver, Settings.System.DEBUG_APP); - boolean waitForDebugger = Settings.System.getInt( - resolver, Settings.System.WAIT_FOR_DEBUGGER, 0) != 0; - boolean alwaysFinishActivities = Settings.System.getInt( - resolver, Settings.System.ALWAYS_FINISH_ACTIVITIES, 0) != 0; + String debugApp = Settings.Global.getString( + resolver, Settings.Global.DEBUG_APP); + boolean waitForDebugger = Settings.Global.getInt( + resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0; + boolean alwaysFinishActivities = Settings.Global.getInt( + resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0; Configuration configuration = new Configuration(); Settings.System.getConfiguration(resolver, configuration); @@ -11119,8 +11119,8 @@ public final class ActivityManagerService extends ActivityManagerNative // instantiated. The backup agent will invoke backupAgentCreated() on the // activity manager to announce its creation. public boolean bindBackupAgent(ApplicationInfo app, int backupMode) { - if (DEBUG_BACKUP) Slog.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode); - enforceCallingPermission("android.permission.BACKUP", "startBackupAgent"); + if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + app + " mode=" + backupMode); + enforceCallingPermission("android.permission.BACKUP", "bindBackupAgent"); synchronized(this) { // !!! TODO: currently no check here that we're already bound @@ -11181,6 +11181,17 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } + @Override + public void clearPendingBackup() { + if (DEBUG_BACKUP) Slog.v(TAG, "clearPendingBackup"); + enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup"); + + synchronized (this) { + mBackupTarget = null; + mBackupAppName = null; + } + } + // A backup agent has just come up public void backupAgentCreated(String agentPackageName, IBinder agent) { if (DEBUG_BACKUP) Slog.v(TAG, "backupAgentCreated: " + agentPackageName @@ -11217,32 +11228,34 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized(this) { - if (mBackupAppName == null) { - Slog.w(TAG, "Unbinding backup agent with no active backup"); - return; - } - - if (!mBackupAppName.equals(appInfo.packageName)) { - Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); - return; - } + try { + if (mBackupAppName == null) { + Slog.w(TAG, "Unbinding backup agent with no active backup"); + return; + } - ProcessRecord proc = mBackupTarget.app; - mBackupTarget = null; - mBackupAppName = null; + if (!mBackupAppName.equals(appInfo.packageName)) { + Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); + return; + } - // Not backing this app up any more; reset its OOM adjustment - updateOomAdjLocked(proc); + // Not backing this app up any more; reset its OOM adjustment + final ProcessRecord proc = mBackupTarget.app; + updateOomAdjLocked(proc); - // If the app crashed during backup, 'thread' will be null here - if (proc.thread != null) { - try { - proc.thread.scheduleDestroyBackupAgent(appInfo, - compatibilityInfoForPackageLocked(appInfo)); - } catch (Exception e) { - Slog.e(TAG, "Exception when unbinding backup agent:"); - e.printStackTrace(); + // If the app crashed during backup, 'thread' will be null here + if (proc.thread != null) { + try { + proc.thread.scheduleDestroyBackupAgent(appInfo, + compatibilityInfoForPackageLocked(appInfo)); + } catch (Exception e) { + Slog.e(TAG, "Exception when unbinding backup agent:"); + e.printStackTrace(); + } } + } finally { + mBackupTarget = null; + mBackupAppName = null; } } } diff --git a/services/java/com/android/server/display/DisplayAdapter.java b/services/java/com/android/server/display/DisplayAdapter.java index abc1d32..b411a0d 100644 --- a/services/java/com/android/server/display/DisplayAdapter.java +++ b/services/java/com/android/server/display/DisplayAdapter.java @@ -42,6 +42,7 @@ abstract class DisplayAdapter { public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2; public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3; + // Called with SyncRoot lock held. public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, String name) { mSyncRoot = syncRoot; diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java index b4dab86..e76bf44 100644 --- a/services/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/java/com/android/server/display/DisplayDeviceInfo.java @@ -17,6 +17,7 @@ package com.android.server.display; import android.util.DisplayMetrics; +import android.view.Surface; import libcore.util.Objects; @@ -31,11 +32,21 @@ final class DisplayDeviceInfo { public static final int FLAG_DEFAULT_DISPLAY = 1 << 0; /** - * Flag: Indicates that this display device can rotate to show contents in a - * different orientation. Otherwise the rotation is assumed to be fixed in the - * natural orientation and the display manager should transform the content to fit. + * Flag: Indicates that the orientation of this display device is coupled to the + * rotation of its associated logical display. + * <p> + * This flag should be applied to the default display to indicate that the user + * physically rotates the display when content is presented in a different orientation. + * The display manager will apply a coordinate transformation assuming that the + * physical orientation of the display matches the logical orientation of its content. + * </p><p> + * The flag should not be set when the display device is mounted in a fixed orientation + * such as on a desk. The display manager will apply a coordinate transformation + * such as a scale and translation to letterbox or pillarbox format under the + * assumption that the physical orientation of the display is invariant. + * </p> */ - public static final int FLAG_SUPPORTS_ROTATION = 1 << 1; + public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 1; /** * Flag: Indicates that this display device has secure video output, such as HDCP. @@ -116,6 +127,17 @@ final class DisplayDeviceInfo { */ public int touch; + /** + * The additional rotation to apply to all content presented on the display device + * relative to its physical coordinate system. Default is {@link Surface#ROTATION_0}. + * <p> + * This field can be used to compensate for the fact that the display has been + * physically rotated relative to its natural orientation such as an HDMI monitor + * that has been mounted sideways to appear to be portrait rather than landscape. + * </p> + */ + public int rotation = Surface.ROTATION_0; + public void setAssumedDensityForExternalDisplay(int width, int height) { densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080; // Technically, these values should be smaller than the apparent density @@ -139,7 +161,8 @@ final class DisplayDeviceInfo { && xDpi == other.xDpi && yDpi == other.yDpi && flags == other.flags - && touch == other.touch; + && touch == other.touch + && rotation == other.rotation; } @Override @@ -157,14 +180,18 @@ final class DisplayDeviceInfo { yDpi = other.yDpi; flags = other.flags; touch = other.touch; + rotation = other.rotation; } // For debugging purposes @Override public String toString() { - return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, " + return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + + refreshRate + " fps, " + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi" - + ", touch " + touchToString(touch) + flagsToString(flags) + "}"; + + ", touch " + touchToString(touch) + flagsToString(flags) + + ", rotation " + rotation + + "}"; } private static String touchToString(int touch) { @@ -185,8 +212,8 @@ final class DisplayDeviceInfo { if ((flags & FLAG_DEFAULT_DISPLAY) != 0) { msg.append(", FLAG_DEFAULT_DISPLAY"); } - if ((flags & FLAG_SUPPORTS_ROTATION) != 0) { - msg.append(", FLAG_SUPPORTS_ROTATION"); + if ((flags & FLAG_ROTATES_WITH_CONTENT) != 0) { + msg.append(", FLAG_ROTATES_WITH_CONTENT"); } if ((flags & FLAG_SECURE) != 0) { msg.append(", FLAG_SECURE"); diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 93896af..e58a0a5 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -127,6 +127,13 @@ public final class DisplayManagerService extends IDisplayManager.Stub { // services should be started. This option may disable certain display adapters. public boolean mOnlyCore; + // True if the display manager service should pretend there is only one display + // and only tell applications about the existence of the default logical display. + // The display manager can still mirror content to secondary displays but applications + // cannot present unique content on those displays. + // Used for demonstration purposes only. + private final boolean mSingleDisplayDemoMode; + // All callback records indexed by calling process id. public final SparseArray<CallbackRecord> mCallbacks = new SparseArray<CallbackRecord>(); @@ -182,6 +189,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub { mHandler = new DisplayManagerHandler(mainHandler.getLooper()); mUiHandler = uiHandler; mDisplayAdapterListener = new DisplayAdapterListener(); + mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER); } @@ -631,6 +639,12 @@ public final class DisplayManagerService extends IDisplayManager.Stub { isDefault = false; } + if (!isDefault && mSingleDisplayDemoMode) { + Slog.i(TAG, "Not creating a logical display for a secondary display " + + " because single display demo mode is enabled: " + deviceInfo); + return; + } + final int displayId = assignDisplayIdLocked(isDefault); final int layerStack = assignLayerStackLocked(displayId); @@ -857,6 +871,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub { pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); pw.println(" mDefaultViewport=" + mDefaultViewport); pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); + pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java index 7ec537f..919733d 100644 --- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java +++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java @@ -29,6 +29,7 @@ import android.util.DisplayMetrics; final class HeadlessDisplayAdapter extends DisplayAdapter { private static final String TAG = "HeadlessDisplayAdapter"; + // Called with SyncRoot lock held. public HeadlessDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { super(syncRoot, context, handler, listener, TAG); diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java index d780006..d6c5248 100644 --- a/services/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/java/com/android/server/display/LocalDisplayAdapter.java @@ -20,6 +20,7 @@ import android.content.Context; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.SystemProperties; import android.util.SparseArray; import android.view.DisplayEventReceiver; import android.view.Surface; @@ -43,21 +44,21 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final SparseArray<LocalDisplayDevice> mDevices = new SparseArray<LocalDisplayDevice>(); - private final HotplugDisplayEventReceiver mHotplugReceiver; + private HotplugDisplayEventReceiver mHotplugReceiver; private final PhysicalDisplayInfo mTempPhys = new PhysicalDisplayInfo(); + // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { super(syncRoot, context, handler, listener, TAG); - mHotplugReceiver = new HotplugDisplayEventReceiver(handler.getLooper()); } @Override public void registerLocked() { - // TODO: listen for notifications from Surface Flinger about - // built-in displays being added or removed and rescan as needed. super.registerLocked(); + + mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper()); scanDisplaysLocked(); } @@ -135,7 +136,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.name = getContext().getResources().getString( com.android.internal.R.string.display_manager_built_in_display_name); mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY - | DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION; + | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f); mInfo.xDpi = mPhys.xDpi; mInfo.yDpi = mPhys.yDpi; @@ -145,6 +146,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { com.android.internal.R.string.display_manager_hdmi_display_name); mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height); + + // For demonstration purposes, allow rotation of the external display. + // In the future we might allow the user to configure this directly. + if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { + mInfo.rotation = Surface.ROTATION_270; + } } } return mInfo; diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java index 680662e..aa7ea82 100644 --- a/services/java/com/android/server/display/LogicalDisplay.java +++ b/services/java/com/android/server/display/LogicalDisplay.java @@ -241,10 +241,13 @@ final class LogicalDisplay { // is rotated when the contents of the logical display are rendered. int orientation = Surface.ROTATION_0; if (device == mPrimaryDisplayDevice - && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) { + && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) { orientation = displayInfo.rotation; } + // Apply the physical rotation of the display device itself. + orientation = (orientation + displayDeviceInfo.rotation) % 4; + // Set the frame. // The frame specifies the rotated physical coordinates into which the viewport // is mapped. We need to take care to preserve the aspect ratio of the viewport. diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java index dfacf2a..937ebcf 100644 --- a/services/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java @@ -64,6 +64,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { new ArrayList<OverlayDisplayHandle>(); private String mCurrentOverlaySetting = ""; + // Called with SyncRoot lock held. public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, Handler uiHandler) { super(syncRoot, context, handler, listener, TAG); diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index c441b02..f9d58af 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -16,17 +16,28 @@ package com.android.server.display; +import com.android.internal.R; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; +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.content.res.Resources; import android.hardware.display.DisplayManager; import android.hardware.display.WifiDisplay; import android.hardware.display.WifiDisplayStatus; import android.media.RemoteDisplay; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.UserHandle; +import android.provider.Settings; import android.util.Slog; import android.view.Surface; @@ -52,8 +63,18 @@ final class WifiDisplayAdapter extends DisplayAdapter { private static final boolean DEBUG = false; + private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1; + private static final int MSG_UPDATE_NOTIFICATION = 2; + + private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT"; + + private final WifiDisplayHandler mHandler; private final PersistentDataStore mPersistentDataStore; private final boolean mSupportsProtectedBuffers; + private final NotificationManager mNotificationManager; + + private PendingIntent mSettingsPendingIntent; + private PendingIntent mDisconnectPendingIntent; private WifiDisplayController mDisplayController; private WifiDisplayDevice mDisplayDevice; @@ -67,14 +88,19 @@ final class WifiDisplayAdapter extends DisplayAdapter { private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; private boolean mPendingStatusChangeBroadcast; + private boolean mPendingNotificationUpdate; + // Called with SyncRoot lock held. public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, PersistentDataStore persistentDataStore) { super(syncRoot, context, handler, listener, TAG); + mHandler = new WifiDisplayHandler(handler.getLooper()); mPersistentDataStore = persistentDataStore; mSupportsProtectedBuffers = context.getResources().getBoolean( com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers); + mNotificationManager = (NotificationManager)context.getSystemService( + Context.NOTIFICATION_SERVICE); } @Override @@ -89,6 +115,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays)); pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays)); pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); + pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate); pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers); // Try to dump the controller state. @@ -113,6 +140,9 @@ final class WifiDisplayAdapter extends DisplayAdapter { public void run() { mDisplayController = new WifiDisplayController( getContext(), getHandler(), mWifiDisplayListener); + + getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, + new IntentFilter(ACTION_DISCONNECT), null, mHandler); } }); } @@ -266,6 +296,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, refreshRate, deviceFlags, surface); sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); + + scheduleUpdateNotificationLocked(); } private void handleDisconnectLocked() { @@ -273,6 +305,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { mDisplayDevice.clearSurfaceLocked(); sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); mDisplayDevice = null; + + scheduleUpdateNotificationLocked(); } } @@ -280,28 +314,100 @@ final class WifiDisplayAdapter extends DisplayAdapter { mCurrentStatus = null; if (!mPendingStatusChangeBroadcast) { mPendingStatusChangeBroadcast = true; - getHandler().post(mStatusChangeBroadcast); + mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST); } } - private final Runnable mStatusChangeBroadcast = new Runnable() { - @Override - public void run() { - final Intent intent; - synchronized (getSyncRoot()) { - if (!mPendingStatusChangeBroadcast) { - return; - } + private void scheduleUpdateNotificationLocked() { + if (!mPendingNotificationUpdate) { + mPendingNotificationUpdate = true; + mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION); + } + } + + // Runs on the handler. + private void handleSendStatusChangeBroadcast() { + final Intent intent; + synchronized (getSyncRoot()) { + if (!mPendingStatusChangeBroadcast) { + return; + } + + mPendingStatusChangeBroadcast = false; + intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, + getWifiDisplayStatusLocked()); + } + + // Send protected broadcast about wifi display status to registered receivers. + getContext().sendBroadcastAsUser(intent, UserHandle.ALL); + } - mPendingStatusChangeBroadcast = false; - intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, - getWifiDisplayStatusLocked()); + // Runs on the handler. + private void handleUpdateNotification() { + final boolean isConnected; + synchronized (getSyncRoot()) { + if (!mPendingNotificationUpdate) { + return; } - // Send protected broadcast about wifi display status to registered receivers. - getContext().sendBroadcast(intent); + mPendingNotificationUpdate = false; + isConnected = (mDisplayDevice != null); + } + + // Cancel the old notification if there is one. + mNotificationManager.cancelAsUser(null, + R.string.wifi_display_notification_title, UserHandle.ALL); + + if (isConnected) { + Context context = getContext(); + + // Initialize pending intents for the notification outside of the lock because + // creating a pending intent requires a call into the activity manager. + if (mSettingsPendingIntent == null) { + Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS); + settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mSettingsPendingIntent = PendingIntent.getActivityAsUser( + context, 0, settingsIntent, 0, null, UserHandle.CURRENT); + } + + if (mDisconnectPendingIntent == null) { + Intent disconnectIntent = new Intent(ACTION_DISCONNECT); + mDisconnectPendingIntent = PendingIntent.getBroadcastAsUser( + context, 0, disconnectIntent, 0, UserHandle.CURRENT); + } + + // Post the notification. + Resources r = context.getResources(); + Notification notification = new Notification.Builder(context) + .setContentTitle(r.getString( + R.string.wifi_display_notification_title)) + .setContentText(r.getString( + R.string.wifi_display_notification_message)) + .setContentIntent(mSettingsPendingIntent) + .setSmallIcon(R.drawable.ic_notify_wifidisplay) + .setOngoing(true) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, + r.getString(R.string.wifi_display_notification_disconnect), + mDisconnectPendingIntent) + .build(); + mNotificationManager.notifyAsUser(null, + R.string.wifi_display_notification_title, + notification, UserHandle.ALL); + } + } + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_DISCONNECT)) { + synchronized (getSyncRoot()) { + requestDisconnectLocked(); + } + } } }; @@ -454,4 +560,23 @@ final class WifiDisplayAdapter extends DisplayAdapter { return mInfo; } } + + private final class WifiDisplayHandler extends Handler { + public WifiDisplayHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SEND_STATUS_CHANGE_BROADCAST: + handleSendStatusChangeBroadcast(); + break; + + case MSG_UPDATE_NOTIFICATION: + handleUpdateNotification(); + break; + } + } + } } diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index 77e6c03..072dd33 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -62,6 +62,8 @@ public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; + private static final boolean DBG = false; + private static final String TAG_NAME = "name"; private static final String ATTR_FLAGS = "flags"; private static final String ATTR_ICON_PATH = "icon"; @@ -97,6 +99,9 @@ public class UserManagerService extends IUserManager.Stub { private int[] mUserIds; private boolean mGuestEnabled; private int mNextSerialNumber; + // This resets on a reboot. Otherwise it keeps incrementing so that user ids are + // not reused in quick succession + private int mNextUserId = MIN_USER_ID; private static UserManagerService sInstance; @@ -199,7 +204,8 @@ public class UserManagerService extends IUserManager.Stub { */ private UserInfo getUserInfoLocked(int userId) { UserInfo ui = mUsers.get(userId); - if (ui != null && ui.partial) { + // If it is partial and not in the process of being removed, return as unknown user. + if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } @@ -668,6 +674,7 @@ public class UserManagerService extends IUserManager.Stub { long now = System.currentTimeMillis(); userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; userInfo.partial = true; + Environment.getUserSystemDirectory(userInfo.id).mkdirs(); mUsers.put(userId, userInfo); writeUserListLocked(); writeUserLocked(userInfo); @@ -709,7 +716,7 @@ public class UserManagerService extends IUserManager.Stub { user.partial = true; writeUserLocked(user); } - + if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); int res; try { res = ActivityManagerNative.getDefault().stopUser(userHandle, @@ -730,12 +737,13 @@ public class UserManagerService extends IUserManager.Stub { } void finishRemoveUser(int userHandle) { + if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle); synchronized (mInstallLock) { synchronized (mPackagesLock) { removeUserStateLocked(userHandle); } } - + if (DBG) Slog.i(LOG_TAG, "Removed user " + userHandle + ", sending broadcast"); // Let other services shutdown any activity long ident = Binder.clearCallingIdentity(); try { @@ -804,10 +812,11 @@ public class UserManagerService extends IUserManager.Stub { num++; } } - int[] newUsers = new int[num]; + final int[] newUsers = new int[num]; + int n = 0; for (int i = 0; i < mUsers.size(); i++) { if (!mUsers.valueAt(i).partial) { - newUsers[i] = mUsers.keyAt(i); + newUsers[n++] = mUsers.keyAt(i); } } mUserIds = newUsers; @@ -840,13 +849,14 @@ public class UserManagerService extends IUserManager.Stub { */ private int getNextAvailableIdLocked() { synchronized (mPackagesLock) { - int i = MIN_USER_ID; + int i = mNextUserId; while (i < Integer.MAX_VALUE) { if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) { break; } i++; } + mNextUserId = i + 1; return i; } } diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index 1561dba..661b949 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -128,28 +128,33 @@ final class DisplayPowerController { private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; // Light sensor event rate in microseconds. - private static final int LIGHT_SENSOR_RATE = 1000000; + private static final int LIGHT_SENSOR_RATE = 500 * 1000; // 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; + private static final int BRIGHTNESS_RAMP_RATE_SLOW = 30; - // 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; + // IIR filter time constants in milliseconds for computing two moving averages of + // the light samples. One is a long-term average and the other is a short-term average. + // We can use these filters to assess trends in ambient brightness. + // The short term average gives us a filtered but relatively low latency measurement. + // The long term average informs us about the overall trend. + private static final long SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 1000; + private static final long LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 8000; // 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. + // are used to debounce the light sensor when adapting to brighter or darker 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; + // an observed change in light level following a previous change in the opposite direction. + private static final long BRIGHTENING_LIGHT_DEBOUNCE = 5000; + private static final long DARKENING_LIGHT_DEBOUNCE = 15000; + + // Hysteresis constraints for brightening or darkening. + // The recent lux must have changed by at least this fraction relative to the + // current ambient lux before a change will be considered. + private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f; + private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f; private final Object mLock = new Object(); @@ -284,39 +289,28 @@ final class DisplayPowerController { // 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; + // The currently accepted nominal ambient light level. + private float mAmbientLux; - // True if recent light samples are getting brighter than the previous - // stable light measurement. - private boolean mRecentLightBrightening; + // True if mAmbientLux holds a valid value. + private boolean mAmbientLuxValid; - // The time constant to use for filtering based on whether the - // light appears to be brightening or dimming. - private long mRecentLightTimeConstant; + // The time when the ambient lux was last brightened or darkened. + private long mLastAmbientBrightenTime; + private long mLastAmbientDarkenTime; // The most recent light sample. - private float mLastLightSample; + private float mLastObservedLux; // The time of the most light recent sample. - private long mLastLightSampleTime; + private long mLastObservedLuxTime; - // The time when we accumulated the first recent light sample into mRecentLightSamples. - private long mFirstRecentLightSampleTime; + // The number of light samples collected since the light sensor was enabled. + private int mRecentLightSamples; - // The upcoming debounce light sensor time. - // This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1. - private long mPendingLightSensorDebounceTime; + // The long-term and short-term filtered light measurements. + private float mRecentShortTermAverageLux; + private float mRecentLongTermAverageLux; // The screen brightness level that has been chosen by the auto-brightness // algorithm. The actual brightness should ramp towards this value. @@ -873,7 +867,8 @@ final class DisplayPowerController { } else { if (mLightSensorEnabled) { mLightSensorEnabled = false; - mLightMeasurementValid = false; + mAmbientLuxValid = false; + mRecentLightSamples = 0; mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED); mSensorManager.unregisterListener(mLightSensorListener); } @@ -884,114 +879,99 @@ final class DisplayPowerController { } 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 filters. + mRecentLightSamples += 1; + if (mRecentLightSamples == 1) { + mRecentShortTermAverageLux = lux; + mRecentLongTermAverageLux = lux; + } else { + final long timeDelta = time - mLastObservedLuxTime; + mRecentShortTermAverageLux += (lux - mRecentShortTermAverageLux) + * timeDelta / (SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta); + mRecentLongTermAverageLux += (lux - mRecentLongTermAverageLux) + * timeDelta / (LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta); } - // 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)); - } + // Remember this sample value. + mLastObservedLux = lux; + mLastObservedLuxTime = time; - // Debounce. + // Update the ambient lux level. 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); + updateAmbientLux(time); } - 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; + private void updateAmbientLux(long time) { + // If the light sensor was just turned on then immediately update our initial + // estimate of the current ambient light level. + if (!mAmbientLuxValid + || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) { + if (DEBUG) { + Slog.d(TAG, "updateAmbientLux: Initializing, " + + "mAmbientLux=" + (mAmbientLuxValid ? mAmbientLux : -1) + + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux + + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux); + } + mAmbientLux = mRecentShortTermAverageLux; + mAmbientLuxValid = true; + mLastAmbientBrightenTime = time; + mLastAmbientDarkenTime = time; + updateAutoBrightness(true); + return; + } + // Determine whether the ambient environment appears to be brightening. + float minAmbientLux = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS); + if (mRecentShortTermAverageLux > minAmbientLux + && mRecentLongTermAverageLux > minAmbientLux) { + long debounceTime = mLastAmbientDarkenTime + BRIGHTENING_LIGHT_DEBOUNCE; + if (time >= debounceTime) { if (DEBUG) { - Slog.d(TAG, "debounceLightSensor: Accepted new measurement " - + mLightMeasurement + " after " - + (now - mFirstRecentLightSampleTime) + " ms based on " - + mRecentLightSamples + " recent samples."); + Slog.d(TAG, "updateAmbientLux: Brightened: " + + "mAmbientLux=" + mAmbientLux + + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux + + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux); } - + mLastAmbientBrightenTime = time; + mAmbientLux = mRecentShortTermAverageLux; updateAutoBrightness(true); + } else { + mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime); + } + return; + } - // 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; + // Determine whether the ambient environment appears to be darkening. + float maxAmbientLux = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS); + if (mRecentShortTermAverageLux < maxAmbientLux + && mRecentLongTermAverageLux < maxAmbientLux) { + long debounceTime = mLastAmbientBrightenTime + DARKENING_LIGHT_DEBOUNCE; + if (time >= debounceTime) { + if (DEBUG) { + Slog.d(TAG, "updateAmbientLux: Darkened: " + + "mAmbientLux=" + mAmbientLux + + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux + + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux); + } + mLastAmbientDarkenTime = time; + mAmbientLux = mRecentShortTermAverageLux; + updateAutoBrightness(true); } else { - Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED); - msg.setAsynchronous(true); - mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime); + mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime); } } } + private void debounceLightSensor() { + updateAmbientLux(SystemClock.uptimeMillis()); + } + private void updateAutoBrightness(boolean sendUpdate) { - if (!mLightMeasurementValid) { + if (!mAmbientLuxValid) { return; } - float value = mScreenAutoBrightnessSpline.interpolate(mLightMeasurement); + float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux); float gamma = 1.0f; if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT @@ -1031,7 +1011,7 @@ final class DisplayPowerController { } int newScreenAutoBrightness = clampScreenBrightness( - (int)Math.round(value * PowerManager.BRIGHTNESS_ON)); + Math.round(value * PowerManager.BRIGHTNESS_ON)); if (mScreenAutoBrightness != newScreenAutoBrightness) { if (DEBUG) { Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" @@ -1152,19 +1132,18 @@ final class DisplayPowerController { 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(" mAmbientLux=" + mAmbientLux); + pw.println(" mAmbientLuxValid=" + mAmbientLuxValid); + pw.println(" mLastAmbientBrightenTime=" + + TimeUtils.formatUptime(mLastAmbientBrightenTime)); + pw.println(" mLastAmbientDimTime=" + + TimeUtils.formatUptime(mLastAmbientDarkenTime)); + pw.println(" mLastObservedLux=" + mLastObservedLux); + pw.println(" mLastObservedLuxTime=" + + TimeUtils.formatUptime(mLastObservedLuxTime)); 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(" mRecentShortTermAverageLux=" + mRecentShortTermAverageLux); + pw.println(" mRecentLongTermAverageLux=" + mRecentLongTermAverageLux); pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness); pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma); diff --git a/services/java/com/android/server/power/PhotonicModulator.java b/services/java/com/android/server/power/PhotonicModulator.java index c9b5d90..648c0c5 100644 --- a/services/java/com/android/server/power/PhotonicModulator.java +++ b/services/java/com/android/server/power/PhotonicModulator.java @@ -16,6 +16,8 @@ package com.android.server.power; +import android.util.Slog; + import com.android.server.LightsService; import java.util.concurrent.Executor; @@ -27,6 +29,9 @@ import java.util.concurrent.Executor; * setting the backlight brightness is especially slow. */ final class PhotonicModulator { + private static final String TAG = "PhotonicModulator"; + private static final boolean DEBUG = false; + private static final int UNKNOWN_LIGHT_VALUE = -1; private final Object mLock = new Object(); @@ -58,6 +63,9 @@ final class PhotonicModulator { synchronized (mLock) { if (lightValue != mPendingLightValue) { mPendingLightValue = lightValue; + if (DEBUG) { + Slog.d(TAG, "Enqueuing request to change brightness to " + lightValue); + } if (!mPendingChange) { mPendingChange = true; mSuspendBlocker.acquire(); @@ -91,6 +99,9 @@ final class PhotonicModulator { } mActualLightValue = newLightValue; } + if (DEBUG) { + Slog.d(TAG, "Setting brightness to " + newLightValue); + } mLight.setBrightness(newLightValue); } } diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 4e692a2..b94bceb 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -107,6 +107,8 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9; // Dirty bit: screen on blocker state became held or unheld private static final int DIRTY_SCREEN_ON_BLOCKER_RELEASED = 1 << 10; + // Dirty bit: dock state changed + private static final int DIRTY_DOCK_STATE = 1 << 11; // 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. @@ -269,6 +271,9 @@ public final class PowerManagerService extends IPowerManager.Stub // draining faster than it is charging and the user activity timeout has expired. private int mBatteryLevelWhenDreamStarted; + // The current dock state. + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + // True if the device should wake up when plugged or unplugged. private boolean mWakeUpWhenPluggedOrUnpluggedConfig; @@ -281,6 +286,9 @@ public final class PowerManagerService extends IPowerManager.Stub // True if dreams should be activated on sleep. private boolean mDreamsActivateOnSleepSetting; + // True if dreams should be activated on dock. + private boolean mDreamsActivateOnDockSetting; + // The screen off timeout setting value in milliseconds. private int mScreenOffTimeoutSetting; @@ -440,6 +448,10 @@ public final class PowerManagerService extends IPowerManager.Stub filter.addAction(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler); + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DOCK_EVENT); + mContext.registerReceiver(new DockReceiver(), filter, null, mHandler); + // Register for settings changes. final ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.Secure.getUriFor( @@ -448,6 +460,9 @@ public final class PowerManagerService extends IPowerManager.Stub resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK), + false, mSettingsObserver, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SCREEN_OFF_TIMEOUT), false, mSettingsObserver, UserHandle.USER_ALL); @@ -487,6 +502,9 @@ public final class PowerManagerService extends IPowerManager.Stub mDreamsActivateOnSleepSetting = (Settings.Secure.getIntForUser(resolver, Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 0, UserHandle.USER_CURRENT) != 0); + mDreamsActivateOnDockSetting = (Settings.Secure.getIntForUser(resolver, + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 0, + UserHandle.USER_CURRENT) != 0); mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver, Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT, UserHandle.USER_CURRENT); @@ -1339,13 +1357,14 @@ public final class PowerManagerService extends IPowerManager.Stub private boolean updateWakefulnessLocked(int dirty) { boolean changed = false; if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED - | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE)) != 0) { + | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE + | DIRTY_DOCK_STATE)) != 0) { if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { if (DEBUG_SPEW) { Slog.d(TAG, "updateWakefulnessLocked: Bed time..."); } final long time = SystemClock.uptimeMillis(); - if (mDreamsActivateOnSleepSetting) { + if (shouldNapAtBedTimeLocked()) { changed = napNoUpdateLocked(time); } else { changed = goToSleepNoUpdateLocked(time, @@ -1357,6 +1376,16 @@ public final class PowerManagerService extends IPowerManager.Stub } /** + * Returns true if the device should automatically nap and start dreaming when the user + * activity timeout has expired and it's bedtime. + */ + private boolean shouldNapAtBedTimeLocked() { + return mDreamsActivateOnSleepSetting + || (mDreamsActivateOnDockSetting + && mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED); + } + + /** * Returns true if the device should go to sleep now. * Also used when exiting a dream to determine whether we should go back * to being fully awake or else go to sleep for good. @@ -2124,6 +2153,7 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" mPlugType=" + mPlugType); pw.println(" mBatteryLevel=" + mBatteryLevel); pw.println(" mBatteryLevelWhenDreamStarted=" + mBatteryLevelWhenDreamStarted); + pw.println(" mDockState=" + mDockState); pw.println(" mStayOn=" + mStayOn); pw.println(" mProximityPositive=" + mProximityPositive); pw.println(" mBootCompleted=" + mBootCompleted); @@ -2149,6 +2179,7 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig); pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); + pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting); pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting); pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin=" + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced=" @@ -2267,6 +2298,21 @@ public final class PowerManagerService extends IPowerManager.Stub } } + 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); + if (mDockState != dockState) { + mDockState = dockState; + mDirty |= DIRTY_DOCK_STATE; + updatePowerStateLocked(); + } + } + } + } + private final class SettingsObserver extends ContentObserver { public SettingsObserver(Handler handler) { super(handler); diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java index 3898ebc..68cdbfc 100644 --- a/services/java/com/android/server/wm/DisplayContent.java +++ b/services/java/com/android/server/wm/DisplayContent.java @@ -66,14 +66,17 @@ class DisplayContent { int mBaseDisplayWidth = 0; int mBaseDisplayHeight = 0; int mBaseDisplayDensity = 0; - final DisplayInfo mDisplayInfo = new DisplayInfo(); - final Display mDisplay; + private final DisplayInfo mDisplayInfo = new DisplayInfo(); + private final Display mDisplay; // Accessed directly by all users. boolean layoutNeeded; int pendingLayoutChanges; final boolean isDefaultDisplay; + /** + * @param display May not be null. + */ DisplayContent(Display display) { mDisplay = display; mDisplayId = display.getDisplayId(); diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java index 545fce5..72fc180 100644 --- a/services/java/com/android/server/wm/DragState.java +++ b/services/java/com/android/server/wm/DragState.java @@ -190,9 +190,11 @@ class DragState { } final WindowList windows = mService.getWindowListLocked(mDisplay); - final int N = windows.size(); - for (int i = 0; i < N; i++) { - sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription); + if (windows != null) { + final int N = windows.size(); + for (int i = 0; i < N; i++) { + sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription); + } } } @@ -393,6 +395,9 @@ class DragState { final int y = (int) yf; final WindowList windows = mService.getWindowListLocked(mDisplay); + if (windows == null) { + return null; + } 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/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index fa450ae..52992a1 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -2092,6 +2092,11 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalStateException("Display has not been initialialized"); } + final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent == null) { + return WindowManagerGlobal.ADD_INVALID_DISPLAY; + } + if (mWindowMap.containsKey(client.asBinder())) { Slog.w(TAG, "Window " + client + " is already added"); return WindowManagerGlobal.ADD_DUPLICATE_ADD; @@ -2174,7 +2179,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - final DisplayContent displayContent = getDisplayContentLocked(displayId); win = new WindowState(this, session, client, token, attachedWindow, seq, attrs, viewVisibility, displayContent); if (win.mDeathRecipient == null) { @@ -2420,6 +2424,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowList windows = win.getWindowList(); windows.remove(win); mPendingRemove.remove(win); + mResizingWindows.remove(win); mWindowsChanged = true; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win); @@ -3384,8 +3389,15 @@ public class WindowManagerService extends IWindowManager.Stub } else { // Exiting app if (scaleUp) { - // noop animation - a = new AlphaAnimation(1, 0); + if (transit == WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN) { + // Fade out while bringing up selected activity. This keeps the + // current activity from showing through a launching wallpaper + // activity. + a = new AlphaAnimation(1, 0); + } else { + // noop animation + a = new AlphaAnimation(1, 1); + } a.setDuration(duration); } else { float scaleW = thumbWidth / displayInfo.appWidth; @@ -5704,6 +5716,7 @@ public class WindowManagerService extends IWindowManager.Stub * @param width the width of the target bitmap * @param height the height of the target bitmap */ + @Override public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height) { if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "screenshotApplications()")) { @@ -5723,6 +5736,9 @@ public class WindowManagerService extends IWindowManager.Stub long ident = Binder.clearCallingIdentity(); final DisplayContent displayContent = getDisplayContentLocked(displayId); + if (displayContent == null) { + return null; + } final DisplayInfo displayInfo = displayContent.getDisplayInfo(); dw = displayInfo.logicalWidth; dh = displayInfo.logicalHeight; @@ -6465,6 +6481,7 @@ public class WindowManagerService extends IWindowManager.Stub return success; } + @Override public void addDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener) { if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, @@ -6473,14 +6490,17 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized(mWindowMap) { DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent.mDisplayContentChangeListeners == null) { - displayContent.mDisplayContentChangeListeners = - new RemoteCallbackList<IDisplayContentChangeListener>(); - displayContent.mDisplayContentChangeListeners.register(listener); + if (displayContent != null) { + if (displayContent.mDisplayContentChangeListeners == null) { + displayContent.mDisplayContentChangeListeners = + new RemoteCallbackList<IDisplayContentChangeListener>(); + displayContent.mDisplayContentChangeListeners.register(listener); + } } } } + @Override public void removeDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener) { if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, @@ -6489,11 +6509,13 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized(mWindowMap) { DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent.mDisplayContentChangeListeners != null) { - displayContent.mDisplayContentChangeListeners.unregister(listener); - if (displayContent.mDisplayContentChangeListeners - .getRegisteredCallbackCount() == 0) { - displayContent.mDisplayContentChangeListeners = null; + if (displayContent != null) { + if (displayContent.mDisplayContentChangeListeners != null) { + displayContent.mDisplayContentChangeListeners.unregister(listener); + if (displayContent.mDisplayContentChangeListeners + .getRegisteredCallbackCount() == 0) { + displayContent.mDisplayContentChangeListeners = null; + } } } } @@ -7149,7 +7171,6 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { final DisplayContent displayContent = getDefaultDisplayContentLocked(); - final Display display = displayContent.getDisplay(); readForcedDisplaySizeAndDensityLocked(displayContent); mDisplayReady = true; @@ -7173,24 +7194,25 @@ public class WindowManagerService extends IWindowManager.Stub } } - public void displayReady(int displayId) { + private void displayReady(int displayId) { synchronized(mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); - final DisplayInfo displayInfo; - mAnimator.addDisplayLocked(displayId); - synchronized(displayContent.mDisplaySizeLock) { - // Bootstrap the default logical display from the display manager. - displayInfo = displayContent.getDisplayInfo(); - DisplayInfo newDisplayInfo = mDisplayManagerService.getDisplayInfo(displayId); - if (newDisplayInfo != null) { - displayInfo.copyFrom(newDisplayInfo); + if (displayContent != null) { + mAnimator.addDisplayLocked(displayId); + synchronized(displayContent.mDisplaySizeLock) { + // Bootstrap the default logical display from the display manager. + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + DisplayInfo newDisplayInfo = mDisplayManagerService.getDisplayInfo(displayId); + if (newDisplayInfo != null) { + displayInfo.copyFrom(newDisplayInfo); + } + displayContent.mInitialDisplayWidth = displayInfo.logicalWidth; + displayContent.mInitialDisplayHeight = displayInfo.logicalHeight; + displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi; + displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth; + displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight; + displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity; } - displayContent.mInitialDisplayWidth = displayInfo.logicalWidth; - displayContent.mInitialDisplayHeight = displayInfo.logicalHeight; - displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi; - displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth; - displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight; - displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity; } } } @@ -7831,12 +7853,15 @@ public class WindowManagerService extends IWindowManager.Stub // TODO(cmautner): Access to DisplayContent should be locked on mWindowMap. Doing that // could lead to deadlock since this is called from ActivityManager. final DisplayContent displayContent = getDisplayContentLocked(displayId); - synchronized(displayContent.mDisplaySizeLock) { - size.x = displayContent.mInitialDisplayWidth; - size.y = displayContent.mInitialDisplayHeight; + if (displayContent != null) { + synchronized(displayContent.mDisplaySizeLock) { + size.x = displayContent.mInitialDisplayWidth; + size.y = displayContent.mInitialDisplayHeight; + } } } + @Override public void setForcedDisplaySize(int displayId, int width, int height) { synchronized(mWindowMap) { // Set some sort of reasonable bounds on the size of the display that we @@ -7845,14 +7870,15 @@ public class WindowManagerService extends IWindowManager.Stub final int MIN_HEIGHT = 200; final int MAX_SCALE = 2; final DisplayContent displayContent = getDisplayContentLocked(displayId); - - width = Math.min(Math.max(width, MIN_WIDTH), - displayContent.mInitialDisplayWidth * MAX_SCALE); - height = Math.min(Math.max(height, MIN_HEIGHT), - displayContent.mInitialDisplayHeight * MAX_SCALE); - setForcedDisplaySizeLocked(displayContent, width, height); - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.DISPLAY_SIZE_FORCED, width + "," + height); + if (displayContent != null) { + width = Math.min(Math.max(width, MIN_WIDTH), + displayContent.mInitialDisplayWidth * MAX_SCALE); + height = Math.min(Math.max(height, MIN_HEIGHT), + displayContent.mInitialDisplayHeight * MAX_SCALE); + setForcedDisplaySizeLocked(displayContent, width, height); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_SIZE_FORCED, width + "," + height); + } } } @@ -7895,6 +7921,7 @@ public class WindowManagerService extends IWindowManager.Stub } } + // displayContent must not be null private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) { Slog.i(TAG, "Using new display size: " + width + "x" + height); @@ -7905,25 +7932,32 @@ public class WindowManagerService extends IWindowManager.Stub reconfigureDisplayLocked(displayContent); } + @Override public void clearForcedDisplaySize(int displayId) { synchronized(mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); - setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth, - displayContent.mInitialDisplayHeight); - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.DISPLAY_SIZE_FORCED, ""); + if (displayContent != null) { + setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth, + displayContent.mInitialDisplayHeight); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_SIZE_FORCED, ""); + } } } + @Override public void setForcedDisplayDensity(int displayId, int density) { synchronized(mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); - setForcedDisplayDensityLocked(displayContent, density); - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density)); + if (displayContent != null) { + setForcedDisplayDensityLocked(displayContent, density); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density)); + } } } + // displayContent must not be null private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) { Slog.i(TAG, "Using new display density: " + density); @@ -7933,15 +7967,19 @@ public class WindowManagerService extends IWindowManager.Stub reconfigureDisplayLocked(displayContent); } + @Override public void clearForcedDisplayDensity(int displayId) { synchronized(mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); - setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity); - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.DISPLAY_DENSITY_FORCED, ""); + if (displayContent != null) { + setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.DISPLAY_DENSITY_FORCED, ""); + } } } + // displayContent must not be null private void reconfigureDisplayLocked(DisplayContent displayContent) { // TODO: Multidisplay: for now only use with default display. mPolicy.setInitialDisplaySize(displayContent.getDisplay(), @@ -9711,7 +9749,9 @@ public class WindowManagerService extends IWindowManager.Stub for (int i = 0; i < count; ++i) { final DisplayContent displayContent = getDisplayContentLocked(pendingLayouts.keyAt(i)); - displayContent.pendingLayoutChanges |= pendingLayouts.valueAt(i); + if (displayContent != null) { + displayContent.pendingLayoutChanges |= pendingLayouts.valueAt(i); + } } mWindowDetachedWallpaper = animToLayout.mWindowDetachedWallpaper; @@ -10837,11 +10877,20 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayContents.put(display.getDisplayId(), displayContent); } + /** + * Retrieve the DisplayContent for the specified displayId. Will create a new DisplayContent if + * there is a Display for the displayId. + * @param displayId The display the caller is interested in. + * @return The DisplayContent associated with displayId or null if there is no Display for it. + */ public DisplayContent getDisplayContentLocked(final int displayId) { DisplayContent displayContent = mDisplayContents.get(displayId); if (displayContent == null) { - displayContent = new DisplayContent(mDisplayManager.getDisplay(displayId)); - mDisplayContents.put(displayId, displayContent); + final Display display = mDisplayManager.getDisplay(displayId); + if (display != null) { + displayContent = new DisplayContent(display); + mDisplayContents.put(displayId, displayContent); + } } return displayContent; } @@ -10927,6 +10976,7 @@ public class WindowManagerService extends IWindowManager.Stub } } + // There is an inherent assumption that this will never return null. public DisplayContent getDefaultDisplayContentLocked() { return getDisplayContentLocked(Display.DEFAULT_DISPLAY); } @@ -10939,8 +10989,14 @@ public class WindowManagerService extends IWindowManager.Stub return getDefaultDisplayContentLocked().getDisplayInfo(); } + /** + * Return the list of WindowStates associated on the passed display. + * @param display The screen to return windows from. + * @return The list of WindowStates on the screen, or null if the there is no screen. + */ public WindowList getWindowListLocked(final Display display) { - return getDisplayContentLocked(display.getDisplayId()).getWindowList(); + final DisplayContent displayContent = getDisplayContentLocked(display.getDisplayId()); + return displayContent != null ? displayContent.getWindowList() : null; } @Override @@ -10949,8 +11005,11 @@ public class WindowManagerService extends IWindowManager.Stub } private void handleDisplayAddedLocked(int displayId) { - createDisplayContentLocked(mDisplayManager.getDisplay(displayId)); - displayReady(displayId); + final Display display = mDisplayManager.getDisplay(displayId); + if (display != null) { + createDisplayContentLocked(display); + displayReady(displayId); + } } @Override @@ -10960,11 +11019,13 @@ public class WindowManagerService extends IWindowManager.Stub private void handleDisplayRemovedLocked(int displayId) { final DisplayContent displayContent = getDisplayContentLocked(displayId); - mDisplayContents.delete(displayId); - WindowList windows = displayContent.getWindowList(); - while (!windows.isEmpty()) { - final WindowState win = windows.get(windows.size() - 1); - removeWindowLocked(win.mSession, win); + if (displayContent != null) { + mDisplayContents.delete(displayId); + WindowList windows = displayContent.getWindowList(); + while (!windows.isEmpty()) { + final WindowState win = windows.get(windows.size() - 1); + removeWindowLocked(win.mSession, win); + } } mAnimator.removeDisplayLocked(displayId); } |
