diff options
Diffstat (limited to 'services/java/com/android')
24 files changed, 1176 insertions, 168 deletions
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index ffc3672..01625dd 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -1470,8 +1470,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { loge("Error modifying route - no interface name"); return false; } - - if (r.isHostRoute() == false) { + if (r.hasGateway()) { RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), r.getGateway()); if (bestRoute != null) { if (bestRoute.getGateway().equals(r.getGateway())) { @@ -2347,28 +2346,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - for (RouteInfo r : routeDiff.added) { - if (isLinkDefault || ! r.isDefaultRoute()) { - addRoute(newLp, r, TO_DEFAULT_TABLE); - } else { - // add to a secondary route table - addRoute(newLp, r, TO_SECONDARY_TABLE); - - // many radios add a default route even when we don't want one. - // remove the default route unless somebody else has asked for it - String ifaceName = newLp.getInterfaceName(); - if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) { - if (VDBG) log("Removing " + r + " for interface " + ifaceName); - try { - mNetd.removeRoute(ifaceName, r); - } catch (Exception e) { - // never crash - catch them all - if (DBG) loge("Exception trying to remove a route: " + e); - } - } - } - } - if (!isLinkDefault) { // handle DNS routes if (routesChanged) { @@ -2393,6 +2370,29 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } } + + for (RouteInfo r : routeDiff.added) { + if (isLinkDefault || ! r.isDefaultRoute()) { + addRoute(newLp, r, TO_DEFAULT_TABLE); + } else { + // add to a secondary route table + addRoute(newLp, r, TO_SECONDARY_TABLE); + + // many radios add a default route even when we don't want one. + // remove the default route unless somebody else has asked for it + String ifaceName = newLp.getInterfaceName(); + if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) { + if (VDBG) log("Removing " + r + " for interface " + ifaceName); + try { + mNetd.removeRoute(ifaceName, r); + } catch (Exception e) { + // never crash - catch them all + if (DBG) loge("Exception trying to remove a route: " + e); + } + } + } + } + return routesChanged; } @@ -3045,7 +3045,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } public void setGlobalProxy(ProxyProperties proxyProperties) { - enforceChangePermission(); + enforceConnectivityInternalPermission(); synchronized (mProxyLock) { if (proxyProperties == mGlobalProxy) return; if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return; @@ -3063,10 +3063,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { mGlobalProxy = null; } ContentResolver res = mContext.getContentResolver(); - Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host); - Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port); - Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, - exclList); + final long token = Binder.clearCallingIdentity(); + try { + Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host); + Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port); + Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, + exclList); + } finally { + Binder.restoreCallingIdentity(token); + } } if (mGlobalProxy == null) { diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index bcb7cb7..b47e8a0 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -54,19 +54,17 @@ import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; import android.util.Log; import android.util.Slog; - -import com.android.internal.app.IAppOpsService; import com.android.internal.content.PackageMonitor; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.server.location.GeocoderProxy; +import com.android.server.location.GeofenceProxy; import com.android.server.location.GeofenceManager; import com.android.server.location.GpsLocationProvider; import com.android.server.location.LocationBlacklist; @@ -118,6 +116,8 @@ public class LocationManagerService extends ILocationManager.Stub { private static final int MSG_LOCATION_CHANGED = 1; + private static final long NANOS_PER_MILLI = 1000000L; + // Location Providers may sometimes deliver location updates // slightly faster that requested - provide grace period so // we don't unnecessarily filter events that are otherwise on @@ -181,6 +181,11 @@ public class LocationManagerService extends ILocationManager.Stub { // mapping from provider name to last known location private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>(); + // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS. + // locations stored here are not fudged for coarse permissions. + private final HashMap<String, Location> mLastLocationCoarseInterval = + new HashMap<String, Location>(); + // all providers that operate over proxy, for authorizing incoming location private final ArrayList<LocationProviderProxy> mProxyProviders = new ArrayList<LocationProviderProxy>(); @@ -338,11 +343,11 @@ public class LocationManagerService extends ILocationManager.Stub { addProviderLocked(passiveProvider); mEnabledProviders.add(passiveProvider.getName()); mPassiveProvider = passiveProvider; + // Create a gps location provider + GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this, + mLocationHandler.getLooper()); if (GpsLocationProvider.isSupported()) { - // Create a gps location provider - GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this, - mLocationHandler.getLooper()); mGpsStatusProvider = gpsProvider.getGpsStatusProvider(); mNetInitiatedListener = gpsProvider.getNetInitiatedListener(); addProviderLocked(gpsProvider); @@ -406,6 +411,14 @@ public class LocationManagerService extends ILocationManager.Stub { if (mGeocodeProvider == null) { Slog.e(TAG, "no geocoder provider found"); } + + // bind to geofence provider + GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, providerPackageNames, + mLocationHandler, gpsProvider.getGpsGeofenceProxy()); + if (provider == null) { + Slog.e(TAG, "no geofence provider found"); + } + } /** @@ -417,6 +430,7 @@ public class LocationManagerService extends ILocationManager.Stub { mLocationHandler.removeMessages(MSG_LOCATION_CHANGED); synchronized (mLock) { mLastLocation.clear(); + mLastLocationCoarseInterval.clear(); for (LocationProviderInterface p : mProviders) { updateProviderListenersLocked(p.getName(), false, mCurrentUserId); } @@ -1401,7 +1415,14 @@ public class LocationManagerService extends ILocationManager.Stub { if (!isAllowedByUserSettingsLocked(name, uid)) return null; - Location location = mLastLocation.get(name); + Location location; + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { + // Make sure that an app with coarse permissions can't get frequent location + // updates by calling LocationManager.getLastKnownLocation repeatedly. + location = mLastLocationCoarseInterval.get(name); + } else { + location = mLastLocation.get(name); + } if (location == null) { return null; } @@ -1667,7 +1688,8 @@ public class LocationManagerService extends ILocationManager.Stub { // Check whether sufficient time has passed long minTime = record.mRequest.getFastestInterval(); - long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) / 1000000L; + long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos()) + / NANOS_PER_MILLI; if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) { return false; } @@ -1720,13 +1742,30 @@ public class LocationManagerService extends ILocationManager.Stub { } lastLocation.set(location); + // Update last known coarse interval location if enough time has passed. + Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider); + if (lastLocationCoarseInterval == null) { + lastLocationCoarseInterval = new Location(location); + mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval); + } + long timeDiffNanos = location.getElapsedRealtimeNanos() + - lastLocationCoarseInterval.getElapsedRealtimeNanos(); + if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) { + lastLocationCoarseInterval.set(location); + } + // Don't ever return a coarse location that is more recent than the allowed update + // interval (i.e. don't allow an app to keep registering and unregistering for + // location updates to overcome the minimum interval). + noGPSLocation = + lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); + // Skip if there are no UpdateRecords for this provider. ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records == null || records.size() == 0) return; // Fetch coarse location Location coarseLocation = null; - if (noGPSLocation != null && !noGPSLocation.equals(lastNoGPSLocation)) { + if (noGPSLocation != null) { coarseLocation = mLocationFudger.getOrCreate(noGPSLocation); } @@ -2015,6 +2054,7 @@ public class LocationManagerService extends ILocationManager.Stub { addProviderLocked(provider); mMockProviders.put(name, provider); mLastLocation.put(name, null); + mLastLocationCoarseInterval.put(name, null); updateProvidersLocked(); } Binder.restoreCallingIdentity(identity); @@ -2037,6 +2077,7 @@ public class LocationManagerService extends ILocationManager.Stub { addProviderLocked(realProvider); } mLastLocation.put(provider, null); + mLastLocationCoarseInterval.put(provider, null); updateProvidersLocked(); Binder.restoreCallingIdentity(identity); } @@ -2168,6 +2209,13 @@ public class LocationManagerService extends ILocationManager.Stub { pw.println(" " + provider + ": " + location); } + pw.println(" Last Known Locations Coarse Intervals:"); + for (Map.Entry<String, Location> entry : mLastLocationCoarseInterval.entrySet()) { + String provider = entry.getKey(); + Location location = entry.getValue(); + pw.println(" " + provider + ": " + location); + } + mGeofenceManager.dump(pw); if (mEnabledProviders.size() > 0) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index cfb892f..fa18e76 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -43,11 +43,13 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.Bitmap; import android.media.AudioManager; import android.media.IAudioService; import android.media.IRingtonePlayer; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -81,6 +83,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.Array; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -264,18 +267,32 @@ public class NotificationManagerService extends INotificationManager.Stub } private static class Archive { - static final int BUFFER_SIZE = 1000; + static final int BUFFER_SIZE = 250; ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE); public Archive() { } + public String toString() { + final StringBuilder sb = new StringBuilder(); + final int N = mBuffer.size(); + sb.append("Archive ("); + sb.append(N); + sb.append(" notification"); + sb.append((N==1)?")":"s)"); + return sb.toString(); + } + public void record(StatusBarNotification nr) { // Nuke heavy parts of notification before storing in archive nr.notification.tickerView = null; nr.notification.contentView = null; nr.notification.bigContentView = null; nr.notification.largeIcon = null; + final Bundle extras = nr.notification.extras; + extras.remove(Notification.EXTRA_LARGE_ICON); + extras.remove(Notification.EXTRA_LARGE_ICON_BIG); + extras.remove(Notification.EXTRA_PICTURE); if (mBuffer.size() == BUFFER_SIZE) { mBuffer.removeFirst(); @@ -283,6 +300,7 @@ public class NotificationManagerService extends INotificationManager.Stub mBuffer.addLast(nr); } + public void clear() { mBuffer.clear(); } @@ -815,22 +833,61 @@ public class NotificationManagerService extends INotificationManager.Stub void dump(PrintWriter pw, String prefix, Context baseContext) { final Notification notification = sbn.notification; pw.println(prefix + this); + pw.println(prefix + " uid=" + sbn.uid + " userId=" + sbn.getUserId()); pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) - + " / " + idDebugString(baseContext, this.sbn.pkg, notification.icon)); - pw.println(prefix + " pri=" + notification.priority); - pw.println(prefix + " score=" + this.sbn.score); + + " / " + idDebugString(baseContext, sbn.pkg, notification.icon)); + pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.score); pw.println(prefix + " contentIntent=" + notification.contentIntent); pw.println(prefix + " deleteIntent=" + notification.deleteIntent); pw.println(prefix + " tickerText=" + notification.tickerText); pw.println(prefix + " contentView=" + notification.contentView); - pw.println(prefix + " uid=" + this.sbn.uid + " userId=" + this.sbn.getUserId()); - pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults)); - pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags)); + pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x", + notification.defaults, notification.flags)); pw.println(prefix + " sound=" + notification.sound); pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); - pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB) - + " ledOnMS=" + notification.ledOnMS - + " ledOffMS=" + notification.ledOffMS); + pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", + notification.ledARGB, notification.ledOnMS, notification.ledOffMS)); + if (notification.actions != null && notification.actions.length > 0) { + pw.println(prefix + " actions={"); + final int N = notification.actions.length; + for (int i=0; i<N; i++) { + final Notification.Action action = notification.actions[i]; + pw.println(String.format("%s [%d] \"%s\" -> %s", + prefix, + i, + action.title, + action.actionIntent.toString() + )); + } + pw.println(prefix + " }"); + } + if (notification.extras != null && notification.extras.size() > 0) { + pw.println(prefix + " extras={"); + for (String key : notification.extras.keySet()) { + pw.print(prefix + " " + key + "="); + Object val = notification.extras.get(key); + if (val == null) { + pw.println("null"); + } else { + pw.print(val.toString()); + if (val instanceof Bitmap) { + pw.print(String.format(" (%dx%d)", + ((Bitmap) val).getWidth(), + ((Bitmap) val).getHeight())); + } else if (val.getClass().isArray()) { + pw.println(" {"); + final int N = Array.getLength(val); + for (int i=0; i<N; i++) { + if (i > 0) pw.println(","); + pw.print(prefix + " " + Array.get(val, i)); + } + pw.print("\n" + prefix + " }"); + } + pw.println(); + } + } + pw.println(prefix + " }"); + } } @Override @@ -2081,7 +2138,7 @@ public class NotificationManagerService extends INotificationManager.Stub if (N > 0) { pw.println(" Lights List:"); for (int i=0; i<N; i++) { - mLights.get(i).dump(pw, " ", mContext); + pw.println(" " + mLights.get(i)); } pw.println(" "); } @@ -2090,6 +2147,17 @@ public class NotificationManagerService extends INotificationManager.Stub pw.println(" mVibrateNotification=" + mVibrateNotification); pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); pw.println(" mSystemReady=" + mSystemReady); + pw.println(" mArchive=" + mArchive.toString()); + Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); + int i=0; + while (iter.hasNext()) { + pw.println(" " + iter.next()); + if (++i >= 5) { + if (iter.hasNext()) pw.println(" ..."); + break; + } + } + } } } diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java index 179db12..0d8a571 100644 --- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -25,6 +25,7 @@ import android.view.Display; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputFilter; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; @@ -80,7 +81,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private final Choreographer mChoreographer; - private int mCurrentDeviceId; + private int mCurrentTouchDeviceId; private boolean mInstalled; @@ -98,6 +99,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private boolean mHoverEventSequenceStarted; + private boolean mKeyEventSequenceStarted; + AccessibilityInputFilter(Context context, AccessibilityManagerService service) { super(context.getMainLooper()); mContext = context; @@ -133,11 +136,21 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo Slog.d(TAG, "Received event: " + event + ", policyFlags=0x" + Integer.toHexString(policyFlags)); } - if (mEventHandler == null) { + if (event instanceof MotionEvent + && event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { + MotionEvent motionEvent = (MotionEvent) event; + onMotionEvent(motionEvent, policyFlags); + } else if (event instanceof KeyEvent + && event.isFromSource(InputDevice.SOURCE_KEYBOARD)) { + KeyEvent keyEvent = (KeyEvent) event; + onKeyEvent(keyEvent, policyFlags); + } else { super.onInputEvent(event, policyFlags); - return; } - if (event.getSource() != InputDevice.SOURCE_TOUCHSCREEN) { + } + + private void onMotionEvent(MotionEvent event, int policyFlags) { + if (mEventHandler == null) { super.onInputEvent(event, policyFlags); return; } @@ -149,26 +162,25 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo return; } final int deviceId = event.getDeviceId(); - if (mCurrentDeviceId != deviceId) { + if (mCurrentTouchDeviceId != deviceId) { + mCurrentTouchDeviceId = deviceId; mMotionEventSequenceStarted = false; mHoverEventSequenceStarted = false; mEventHandler.clear(); - mCurrentDeviceId = deviceId; } - if (mCurrentDeviceId < 0) { + if (mCurrentTouchDeviceId < 0) { super.onInputEvent(event, policyFlags); return; } // We do not handle scroll events. - MotionEvent motionEvent = (MotionEvent) event; - if (motionEvent.getActionMasked() == MotionEvent.ACTION_SCROLL) { + if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) { super.onInputEvent(event, policyFlags); return; } // Wait for a down touch event to start processing. - if (motionEvent.isTouchEvent()) { + if (event.isTouchEvent()) { if (!mMotionEventSequenceStarted) { - if (motionEvent.getActionMasked() != MotionEvent.ACTION_DOWN) { + if (event.getActionMasked() != MotionEvent.ACTION_DOWN) { return; } mMotionEventSequenceStarted = true; @@ -176,7 +188,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } else { // Wait for an enter hover event to start processing. if (!mHoverEventSequenceStarted) { - if (motionEvent.getActionMasked() != MotionEvent.ACTION_HOVER_ENTER) { + if (event.getActionMasked() != MotionEvent.ACTION_HOVER_ENTER) { return; } mHoverEventSequenceStarted = true; @@ -185,6 +197,22 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo batchMotionEvent((MotionEvent) event, policyFlags); } + private void onKeyEvent(KeyEvent event, int policyFlags) { + if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) { + mKeyEventSequenceStarted = false; + super.onInputEvent(event, policyFlags); + return; + } + // Wait for a down key event to start processing. + if (!mKeyEventSequenceStarted) { + if (event.getAction() != KeyEvent.ACTION_DOWN) { + return; + } + mKeyEventSequenceStarted = true; + } + mAms.notifyKeyEvent(event, policyFlags); + } + private void scheduleProcessBatchedEvents() { mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, mProcessBatchedEventsRunnable, null); @@ -286,6 +314,13 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } + void reset() { + setEnabledFeatures(0); + mKeyEventSequenceStarted = false; + mMotionEventSequenceStarted = false; + mHoverEventSequenceStarted = false; + } + private void enableFeatures() { mMotionEventSequenceStarted = false; mHoverEventSequenceStarted = false; diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 527e891..1f3ac96 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -61,16 +61,20 @@ import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; +import android.util.Pools.Pool; +import android.util.Pools.SimplePool; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.IWindow; import android.view.IWindowManager; import android.view.InputDevice; +import android.view.InputEventConsistencyVerifier; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MagnificationSpec; import android.view.WindowManager; +import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityManager; @@ -132,6 +136,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private static final int OWN_PROCESS_ID = android.os.Process.myPid(); + private static final int MAX_POOL_SIZE = 10; + private static int sIdCounter = 0; private static int sNextWindowId; @@ -140,6 +146,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Object mLock = new Object(); + private final Pool<PendingEvent> mPendingEventPool = + new SimplePool<PendingEvent>(MAX_POOL_SIZE); + private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); @@ -633,6 +642,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + boolean notifyKeyEvent(KeyEvent event, int policyFlags) { + synchronized (mLock) { + KeyEvent localClone = KeyEvent.obtain(event); + boolean handled = notifyKeyEventLocked(localClone, policyFlags, false); + if (!handled) { + handled = notifyKeyEventLocked(localClone, policyFlags, true); + } + return handled; + } + } + /** * Gets the bounds of the accessibility focus in the active window. * @@ -798,6 +818,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } + private boolean notifyKeyEventLocked(KeyEvent event, int policyFlags, boolean isDefault) { + // TODO: Now we are giving the key events to the last enabled + // service that can handle them which is the last one + // in our list since we write the last enabled as the + // last record in the enabled services setting. Ideally, + // the user should make the call which service handles + // key events. However, only one service should handle + // key events to avoid user frustration when different + // behavior is observed from different combinations of + // enabled accessibility services. + UserState state = getCurrentUserStateLocked(); + for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { + Service service = state.mBoundServices.get(i); + if (service.mIsDefault == isDefault) { + service.notifyKeyEvent(event, policyFlags); + return true; + } + } + return false; + } + private void notifyClearAccessibilityNodeInfoCacheLocked() { UserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { @@ -929,7 +970,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void addServiceLocked(Service service, UserState userState) { try { - service.linkToOwnDeath(); + service.linkToOwnDeathLocked(); userState.mBoundServices.add(service); userState.mComponentNameToServiceMap.put(service.mComponentName, service); } catch (RemoteException re) { @@ -946,7 +987,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void removeServiceLocked(Service service, UserState userState) { userState.mBoundServices.remove(service); userState.mComponentNameToServiceMap.remove(service.mComponentName); - service.unlinkToOwnDeath(); + service.unlinkToOwnDeathLocked(); } /** @@ -1119,8 +1160,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { boolean setInputFilter = false; AccessibilityInputFilter inputFilter = null; synchronized (mLock) { - if ((userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled) - || userState.mIsDisplayMagnificationEnabled) { + if (userState.mIsAccessibilityEnabled) { if (!mHasInputFilter) { mHasInputFilter = true; if (mInputFilter == null) { @@ -1141,7 +1181,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } else { if (mHasInputFilter) { mHasInputFilter = false; - mInputFilter.setEnabledFeatures(0); + mInputFilter.reset(); inputFilter = null; setInputFilter = true; } @@ -1446,6 +1486,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public static final int MSG_ANNOUNCE_NEW_USER_IF_NEEDED = 5; public static final int MSG_UPDATE_INPUT_FILTER = 6; public static final int MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG = 7; + public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8; public MainHandler(Looper looper) { super(looper); @@ -1464,6 +1505,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } event.recycle(); } break; + case MSG_SEND_KEY_EVENT_TO_INPUT_FILTER: { + KeyEvent event = (KeyEvent) msg.obj; + final int policyFlags = msg.arg1; + synchronized (mLock) { + if (mHasInputFilter && mInputFilter != null) { + mInputFilter.sendInputEvent(event, policyFlags); + } + } + event.recycle(); + } break; case MSG_SEND_STATE_TO_CLIENTS: { final int clientState = msg.arg1; final int userId = msg.arg2; @@ -1536,6 +1587,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private PendingEvent obtainPendingEventLocked(KeyEvent event, int policyFlags, int sequence) { + PendingEvent pendingEvent = mPendingEventPool.acquire(); + if (pendingEvent == null) { + pendingEvent = new PendingEvent(); + } + pendingEvent.event = event; + pendingEvent.policyFlags = policyFlags; + pendingEvent.sequence = sequence; + return pendingEvent; + } + + private void recyclePendingEventLocked(PendingEvent pendingEvent) { + pendingEvent.clear(); + mPendingEventPool.release(pendingEvent); + } + /** * This class represents an accessibility service. It stores all per service * data required for the service management, provides API for starting/stopping the @@ -1545,12 +1612,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { * connection for the service. */ class Service extends IAccessibilityServiceConnection.Stub - implements ServiceConnection, DeathRecipient { - - // We pick the MSBs to avoid collision since accessibility event types are - // used as message types allowing us to remove messages per event type. - private static final int MSG_ON_GESTURE = 0x80000000; - private static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 0x40000000; + implements ServiceConnection, DeathRecipient {; final int mUserId; @@ -1594,29 +1656,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<AccessibilityEvent>(); - /** - * Handler for delayed event dispatch. - */ - public Handler mHandler = new Handler(mMainHandler.getLooper()) { + final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher(); + + // Handler only for dispatching accessibility events since we use event + // types as message types allowing us to remove messages per event type. + public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) { @Override public void handleMessage(Message message) { - final int type = message.what; - switch (type) { - case MSG_ON_GESTURE: { - final int gestureId = message.arg1; - notifyGestureInternal(gestureId); - } break; - case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: { - notifyClearAccessibilityNodeInfoCacheInternal(); - } break; - default: { - final int eventType = type; - notifyAccessibilityEventInternal(eventType); - } break; - } + final int eventType = message.what; + notifyAccessibilityEventInternal(eventType); } }; + // Handler for scheduling method invocations on the main thread. + public InvocationHandler mInvocationHandler = new InvocationHandler( + mMainHandler.getLooper()); + public Service(int userId, ComponentName componentName, AccessibilityServiceInfo accessibilityServiceInfo) { mUserId = userId; @@ -1703,13 +1758,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } UserState userState = getUserStateLocked(mUserId); + mKeyEventDispatcher.flush(); if (!mIsAutomation) { mContext.unbindService(this); } else { userState.destroyUiAutomationService(); } removeServiceLocked(this, userState); - dispose(); + resetLocked(); return true; } @@ -1718,6 +1774,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override + public void setOnKeyEventResult(boolean handled, int sequence) { + mKeyEventDispatcher.setOnKeyEventResult(handled, sequence); + } + + @Override public AccessibilityServiceInfo getServiceInfo() { synchronized (mLock) { return mAccessibilityServiceInfo; @@ -1756,11 +1817,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { addServiceLocked(this, userState); if (userState.mBindingServices.contains(mComponentName)) { userState.mBindingServices.remove(mComponentName); - onUserStateChangedLocked(userState); try { - mServiceInterface.setConnection(this, mId); + mServiceInterface.setConnection(this, mId); + onUserStateChangedLocked(userState); } catch (RemoteException re) { - Slog.w(LOG_TAG, "Error while setting connection for service: " + service, re); + Slog.w(LOG_TAG, "Error while setting connection for service: " + + service, re); + binderDied(); } } else { binderDied(); @@ -2087,15 +2150,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { /* do nothing - #binderDied takes care */ } - public void linkToOwnDeath() throws RemoteException { + public void linkToOwnDeathLocked() throws RemoteException { mService.linkToDeath(this, 0); } - public void unlinkToOwnDeath() { + public void unlinkToOwnDeathLocked() { mService.unlinkToDeath(this, 0); } - public void dispose() { + public void resetLocked() { try { // Clear the proxy in the other process so this // IAccessibilityServiceConnection can be garbage collected. @@ -2107,12 +2170,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mServiceInterface = null; } + public boolean isInitializedLocked() { + return (mService != null); + } + public void binderDied() { synchronized (mLock) { + // It is possible that this service's package was force stopped during + // whose handling the death recipient is unlinked and still get a call + // on binderDied since the call was made before we unlink but was + // waiting on the lock we held during the force stop handling. + if (!isInitializedLocked()) { + return; + } + mKeyEventDispatcher.flush(); UserState userState = getUserStateLocked(mUserId); // The death recipient is unregistered in removeServiceLocked removeServiceLocked(this, userState); - dispose(); + resetLocked(); if (mIsAutomation) { // We no longer have an automation service, so restore // the state based on values in the settings database. @@ -2141,12 +2216,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int what = eventType; if (oldEvent != null) { - mHandler.removeMessages(what); + mEventDispatchHandler.removeMessages(what); oldEvent.recycle(); } - Message message = mHandler.obtainMessage(what); - mHandler.sendMessageDelayed(message, mNotificationTimeout); + Message message = mEventDispatchHandler.obtainMessage(what); + mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout); } } @@ -2211,11 +2286,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } public void notifyGesture(int gestureId) { - mHandler.obtainMessage(MSG_ON_GESTURE, gestureId, 0).sendToTarget(); + mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, + gestureId, 0).sendToTarget(); + } + + public void notifyKeyEvent(KeyEvent event, int policyFlags) { + mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_KEY_EVENT, + policyFlags, 0, event).sendToTarget(); } public void notifyClearAccessibilityNodeInfoCache() { - mHandler.sendEmptyMessage(MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE); + mInvocationHandler.sendEmptyMessage( + InvocationHandler.MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE); } private void notifyGestureInternal(int gestureId) { @@ -2230,6 +2312,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private void notifyKeyEventInternal(KeyEvent event, int policyFlags) { + mKeyEventDispatcher.notifyKeyEvent(event, policyFlags); + } + private void notifyClearAccessibilityNodeInfoCacheInternal() { IAccessibilityServiceClient listener = mServiceInterface; if (listener != null) { @@ -2339,6 +2425,179 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } return null; } + + private final class InvocationHandler extends Handler { + + public static final int MSG_ON_GESTURE = 1; + public static final int MSG_ON_KEY_EVENT = 2; + public static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 3; + public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4; + + public InvocationHandler(Looper looper) { + super(looper, null, true); + } + + @Override + public void handleMessage(Message message) { + final int type = message.what; + switch (type) { + case MSG_ON_GESTURE: { + final int gestureId = message.arg1; + notifyGestureInternal(gestureId); + } break; + case MSG_ON_KEY_EVENT: { + KeyEvent event = (KeyEvent) message.obj; + final int policyFlags = message.arg1; + notifyKeyEventInternal(event, policyFlags); + } break; + case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: { + notifyClearAccessibilityNodeInfoCacheInternal(); + } break; + case MSG_ON_KEY_EVENT_TIMEOUT: { + PendingEvent eventState = (PendingEvent) message.obj; + setOnKeyEventResult(false, eventState.sequence); + } break; + default: { + throw new IllegalArgumentException("Unknown message: " + type); + } + } + } + } + + private final class KeyEventDispatcher { + + private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500; + + private PendingEvent mPendingEvents; + + private final InputEventConsistencyVerifier mSentEventsVerifier = + InputEventConsistencyVerifier.isInstrumentationEnabled() + ? new InputEventConsistencyVerifier( + this, 0, KeyEventDispatcher.class.getSimpleName()) : null; + + public void notifyKeyEvent(KeyEvent event, int policyFlags) { + final PendingEvent pendingEvent; + + synchronized (mLock) { + pendingEvent = addPendingEventLocked(event, policyFlags); + } + + Message message = mInvocationHandler.obtainMessage( + InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent); + mInvocationHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS); + + try { + // Accessibility services are exclusively not in the system + // process, therefore no need to clone the motion event to + // prevent tampering. It will be cloned in the IPC call. + mServiceInterface.onKeyEvent(pendingEvent.event, pendingEvent.sequence); + } catch (RemoteException re) { + setOnKeyEventResult(false, pendingEvent.sequence); + } + } + + public void setOnKeyEventResult(boolean handled, int sequence) { + synchronized (mLock) { + PendingEvent pendingEvent = removePendingEventLocked(sequence); + if (pendingEvent != null) { + mInvocationHandler.removeMessages( + InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT, + pendingEvent); + pendingEvent.handled = handled; + finishPendingEventLocked(pendingEvent); + } + } + } + + public void flush() { + synchronized (mLock) { + cancelAllPendingEventsLocked(); + if (mSentEventsVerifier != null) { + mSentEventsVerifier.reset(); + } + } + } + + private PendingEvent addPendingEventLocked(KeyEvent event, int policyFlags) { + final int sequence = event.getSequenceNumber(); + PendingEvent pendingEvent = obtainPendingEventLocked(event, policyFlags, sequence); + pendingEvent.next = mPendingEvents; + mPendingEvents = pendingEvent; + return pendingEvent; + } + + private PendingEvent removePendingEventLocked(int sequence) { + PendingEvent previous = null; + PendingEvent current = mPendingEvents; + + while (current != null) { + if (current.sequence == sequence) { + if (previous != null) { + previous.next = current.next; + } else { + mPendingEvents = current.next; + } + current.next = null; + return current; + } + previous = current; + current = current.next; + } + return null; + } + + private void finishPendingEventLocked(PendingEvent pendingEvent) { + if (!pendingEvent.handled) { + sendKeyEventToInputFilter(pendingEvent.event, pendingEvent.policyFlags); + } + // Nullify the event since we do not want it to be + // recycled yet. It will be sent to the input filter. + pendingEvent.event = null; + recyclePendingEventLocked(pendingEvent); + } + + private void sendKeyEventToInputFilter(KeyEvent event, int policyFlags) { + if (DEBUG) { + Slog.i(LOG_TAG, "Injecting event: " + event); + } + if (mSentEventsVerifier != null) { + mSentEventsVerifier.onKeyEvent(event, 0); + } + policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; + mMainHandler.obtainMessage(MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, + policyFlags, 0, event).sendToTarget(); + } + + private void cancelAllPendingEventsLocked() { + while (mPendingEvents != null) { + PendingEvent pendingEvent = removePendingEventLocked(mPendingEvents.sequence); + pendingEvent.handled = false; + mInvocationHandler.removeMessages(InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT, + pendingEvent); + finishPendingEventLocked(pendingEvent); + } + } + } + } + + private static final class PendingEvent { + PendingEvent next; + + KeyEvent event; + int policyFlags; + int sequence; + boolean handled; + + public void clear() { + if (event != null) { + event.recycle(); + event = null; + } + next = null; + policyFlags = 0; + sequence = 0; + handled = false; + } } final class SecurityPolicy { diff --git a/services/java/com/android/server/accessibility/EventStreamTransformation.java b/services/java/com/android/server/accessibility/EventStreamTransformation.java index 3289a15..8c93e7b 100644 --- a/services/java/com/android/server/accessibility/EventStreamTransformation.java +++ b/services/java/com/android/server/accessibility/EventStreamTransformation.java @@ -57,7 +57,7 @@ import android.view.accessibility.AccessibilityEvent; interface EventStreamTransformation { /** - * Receives motion event. Passed are the event transformed by previous + * Receives a motion event. Passed are the event transformed by previous * transformations and the raw event to which no transformations have * been applied. * diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java index 14d808f..fd7cd78 100644 --- a/services/java/com/android/server/accounts/AccountManagerService.java +++ b/services/java/com/android/server/accounts/AccountManagerService.java @@ -22,6 +22,7 @@ import android.accounts.AccountAndUser; import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; +import android.accounts.CantAddAccountActivity; import android.accounts.GrantCredentialsPermissionActivity; import android.accounts.IAccountAuthenticator; import android.accounts.IAccountAuthenticatorResponse; @@ -1456,6 +1457,14 @@ public class AccountManagerService "User is not allowed to add an account!"); } catch (RemoteException re) { } + Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); + cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + long identityToken = clearCallingIdentity(); + try { + mContext.startActivityAsUser(cantAddAccount, UserHandle.CURRENT); + } finally { + restoreCallingIdentity(identityToken); + } return; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index cc7905c..bc1df85 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -936,6 +936,12 @@ public final class ActivityManagerService extends ActivityManagerNative CompatModeDialog mCompatModeDialog; long mLastMemUsageReportTime = 0; + /** + * Flag whether the current user is a "monkey", i.e. whether + * the UI is driven by a UI automation tool. + */ + private boolean mUserIsMonkey; + final Handler mHandler = new Handler() { //public Handler() { // if (localLOGV) Slog.v(TAG, "Handler started!"); @@ -4577,7 +4583,7 @@ public final class ActivityManagerService extends ActivityManagerNative public String getCallingPackage(IBinder token) { synchronized (this) { ActivityRecord r = getCallingRecordLocked(token); - return r != null && r.app != null ? r.info.packageName : null; + return r != null ? r.info.packageName : null; } } @@ -7434,11 +7440,27 @@ public final class ActivityManagerService extends ActivityManagerNative } } + public void setUserIsMonkey(boolean userIsMonkey) { + synchronized (this) { + synchronized (mPidsSelfLocked) { + final int callingPid = Binder.getCallingPid(); + ProcessRecord precessRecord = mPidsSelfLocked.get(callingPid); + if (precessRecord == null) { + throw new SecurityException("Unknown process: " + callingPid); + } + if (precessRecord.instrumentationUiAutomationConnection == null) { + throw new SecurityException("Only an instrumentation process " + + "with a UiAutomation can call setUserIsMonkey"); + } + } + mUserIsMonkey = userIsMonkey; + } + } + public boolean isUserAMonkey() { - // For now the fact that there is a controller implies - // we have a monkey. synchronized (this) { - return mController != null; + // If there is a controller also implies the user is a monkey. + return (mUserIsMonkey || mController != null); } } @@ -12435,6 +12457,9 @@ public final class ActivityManagerService extends ActivityManagerNative } catch (RemoteException re) { /* ignore */ } + // Only a UiAutomation can set this flag and now that + // it is finished we make sure it is reset to its default. + mUserIsMonkey = false; } app.instrumentationWatcher = null; app.instrumentationUiAutomationConnection = null; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 3d2e912..0f1700d 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -47,6 +47,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -4600,11 +4601,13 @@ final class ActivityStack { private final void logStartActivity(int tag, ActivityRecord r, TaskRecord task) { + final Uri data = r.intent.getData(); + final String strData = data != null ? data.toSafeString() : null; + EventLog.writeEvent(tag, r.userId, System.identityHashCode(r), task.taskId, r.shortComponentName, r.intent.getAction(), - r.intent.getType(), r.intent.getDataString(), - r.intent.getFlags()); + r.intent.getType(), strData, r.intent.getFlags()); } /** diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java index 68cf5fc..f82cf01 100644 --- a/services/java/com/android/server/content/ContentService.java +++ b/services/java/com/android/server/content/ContentService.java @@ -459,7 +459,7 @@ public final class ContentService extends IContentService.Stub { try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - return syncManager.getSyncStorageEngine().getIsSyncable( + return syncManager.getIsSyncable( account, userId, providerName); } } finally { diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java index b3f9bf1..1c883ec 100644 --- a/services/java/com/android/server/content/SyncManager.java +++ b/services/java/com/android/server/content/SyncManager.java @@ -21,6 +21,7 @@ import android.accounts.AccountAndUser; import android.accounts.AccountManager; import android.app.ActivityManager; import android.app.AlarmManager; +import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -41,6 +42,7 @@ import android.content.SyncInfo; import android.content.SyncResult; import android.content.SyncStatusInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.RegisteredServicesCache; @@ -491,6 +493,36 @@ public class SyncManager { return mSyncStorageEngine; } + public int getIsSyncable(Account account, int userId, String providerName) { + int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName); + UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId); + + // If it's not a restricted user, return isSyncable + if (userInfo == null || !userInfo.isRestricted()) return isSyncable; + + // Else check if the sync adapter has opted-in or not + RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = + mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(providerName, account.type), userId); + if (syncAdapterInfo == null) return isSyncable; + + PackageInfo pInfo = null; + try { + pInfo = AppGlobals.getPackageManager().getPackageInfo( + syncAdapterInfo.componentName.getPackageName(), 0, userId); + if (pInfo == null) return isSyncable; + } catch (RemoteException re) { + // Shouldn't happen + return isSyncable; + } + if (pInfo.restrictedAccountType != null + && pInfo.restrictedAccountType.equals(account.type)) { + return isSyncable; + } else { + return 0; + } + } + private void ensureAlarmService() { if (mAlarmService == null) { mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); @@ -608,7 +640,7 @@ public class SyncManager { } for (String authority : syncableAuthorities) { - int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId, + int isSyncable = getIsSyncable(account.account, account.userId, authority); if (isSyncable == 0) { continue; @@ -1930,7 +1962,7 @@ public class SyncManager { continue; } - if (mSyncStorageEngine.getIsSyncable(info.account, info.userId, info.authority) + if (getIsSyncable(info.account, info.userId, info.authority) == 0) { continue; } @@ -2069,7 +2101,7 @@ public class SyncManager { } // drop this sync request if it isn't syncable - int syncableState = mSyncStorageEngine.getIsSyncable( + int syncableState = getIsSyncable( op.account, op.userId, op.authority); if (syncableState == 0) { operationIterator.remove(); diff --git a/services/java/com/android/server/location/GeofenceProxy.java b/services/java/com/android/server/location/GeofenceProxy.java new file mode 100644 index 0000000..36e9fcc --- /dev/null +++ b/services/java/com/android/server/location/GeofenceProxy.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.location; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.hardware.location.GeofenceHardwareService; +import android.hardware.location.IGeofenceHardware; +import android.location.IGeofenceProvider; +import android.location.IGpsGeofenceHardware; +import android.content.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import com.android.server.ServiceWatcher; + +import java.util.List; + +/** + * @hide + */ +public final class GeofenceProxy { + private static final String TAG = "GeofenceProxy"; + private static final String SERVICE_ACTION = + "com.android.location.service.GeofenceProvider"; + private ServiceWatcher mServiceWatcher; + private Context mContext; + private IGeofenceHardware mGeofenceHardware; + private IGpsGeofenceHardware mGpsGeofenceHardware; + + private static final int GEOFENCE_PROVIDER_CONNECTED = 1; + private static final int GEOFENCE_HARDWARE_CONNECTED = 2; + private static final int GEOFENCE_HARDWARE_DISCONNECTED = 3; + private static final int GEOFENCE_GPS_HARDWARE_CONNECTED = 4; + private static final int GEOFENCE_GPS_HARDWARE_DISCONNECTED = 5; + + private Runnable mRunnable = new Runnable() { + @Override + public void run() { + mHandler.sendEmptyMessage(GEOFENCE_PROVIDER_CONNECTED); + } + }; + + public static GeofenceProxy createAndBind(Context context, + List<String> initialPackageNames, Handler handler, IGpsGeofenceHardware gpsGeofence) { + GeofenceProxy proxy = new GeofenceProxy(context, initialPackageNames, handler, gpsGeofence); + if (proxy.bindGeofenceProvider()) { + return proxy; + } else { + return null; + } + } + + private GeofenceProxy(Context context, List<String> initialPackageName, Handler handler, + IGpsGeofenceHardware gpsGeofence) { + mContext = context; + mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, initialPackageName, + mRunnable, handler); + mGpsGeofenceHardware = gpsGeofence; + bindHardwareGeofence(); + } + + private boolean bindGeofenceProvider() { + return mServiceWatcher.start(); + } + + private IGeofenceProvider getGeofenceProviderService() { + return IGeofenceProvider.Stub.asInterface(mServiceWatcher.getBinder()); + } + + private void bindHardwareGeofence() { + mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class), + mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER); + } + + private ServiceConnection mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mGeofenceHardware = IGeofenceHardware.Stub.asInterface(service); + mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_CONNECTED); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mGeofenceHardware = null; + mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_DISCONNECTED); + } + }; + + private void setGeofenceHardwareInProvider() { + try { + getGeofenceProviderService().setGeofenceHardware(mGeofenceHardware); + } catch (RemoteException e) { + Log.e(TAG, "Remote Exception: setGeofenceHardwareInProvider: " + e); + } + } + + private void setGpsGeofence() { + try { + mGeofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware); + } catch (RemoteException e) { + Log.e(TAG, "Error while connecting to GeofenceHardwareService"); + } + } + + + // This needs to be reworked, when more services get added, + // Might need a state machine or add a framework utility class, + private Handler mHandler = new Handler() { + private boolean mGeofenceHardwareConnected = false; + private boolean mGeofenceProviderConnected = false; + + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case GEOFENCE_PROVIDER_CONNECTED: + mGeofenceProviderConnected = true; + if (mGeofenceHardwareConnected) { + setGeofenceHardwareInProvider(); + } + break; + case GEOFENCE_HARDWARE_CONNECTED: + setGpsGeofence(); + mGeofenceHardwareConnected = true; + if (mGeofenceProviderConnected) { + setGeofenceHardwareInProvider(); + } + break; + case GEOFENCE_HARDWARE_DISCONNECTED: + mGeofenceHardwareConnected = false; + setGeofenceHardwareInProvider(); + break; + } + } + }; +} diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 3552b6a..8c88cab 100644 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -24,7 +24,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; +import android.hardware.location.GeofenceHardwareImpl; +import android.hardware.location.IGeofenceHardware; import android.location.Criteria; +import android.location.IGpsGeofenceHardware; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; import android.location.ILocationManager; @@ -33,6 +36,7 @@ import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; +import android.location.LocationRequest; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; @@ -259,6 +263,9 @@ public class GpsLocationProvider implements LocationProviderInterface { // true if we started navigation private boolean mStarted; + // true if single shot request is in progress + private boolean mSingleShot; + // capabilities of the GPS engine private int mEngineCapabilities; @@ -314,6 +321,8 @@ public class GpsLocationProvider implements LocationProviderInterface { // only modified on handler thread private WorkSource mClientSource = new WorkSource(); + private GeofenceHardwareImpl mGeofenceHardwareImpl; + private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() { @Override public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException { @@ -367,13 +376,17 @@ public class GpsLocationProvider implements LocationProviderInterface { return mGpsStatusProvider; } + public IGpsGeofenceHardware getGpsGeofenceProxy() { + return mGpsGeofenceBinder; + } + private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(ALARM_WAKEUP)) { if (DEBUG) Log.d(TAG, "ALARM_WAKEUP"); - startNavigating(); + startNavigating(false); } else if (action.equals(ALARM_TIMEOUT)) { if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT"); hibernate(); @@ -794,10 +807,22 @@ public class GpsLocationProvider implements LocationProviderInterface { } private void handleSetRequest(ProviderRequest request, WorkSource source) { - if (DEBUG) Log.d(TAG, "setRequest " + request); + boolean singleShot = false; + // see if the request is for a single update + if (request.locationRequests != null && request.locationRequests.size() > 0) { + // if any request has zero or more than one updates + // requested, then this is not single-shot mode + singleShot = true; + for (LocationRequest lr : request.locationRequests) { + if (lr.getNumUpdates() != 1) { + singleShot = false; + } + } + } + if (DEBUG) Log.d(TAG, "setRequest " + request); if (request.reportLocation) { // update client uids updateClientUids(source); @@ -819,7 +844,7 @@ public class GpsLocationProvider implements LocationProviderInterface { } } else if (!mStarted) { // start GPS - startNavigating(); + startNavigating(singleShot); } } else { updateClientUids(new WorkSource()); @@ -918,6 +943,31 @@ public class GpsLocationProvider implements LocationProviderInterface { return result; } + private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() { + public boolean isHardwareGeofenceSupported() { + return native_is_geofence_supported(); + } + + public boolean addCircularHardwareGeofence(int geofenceId, double latitude, + double longitude, double radius, int lastTransition, int monitorTransitions, + int notificationResponsiveness, int unknownTimer) { + return native_add_geofence(geofenceId, latitude, longitude, radius, + lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer); + } + + public boolean removeHardwareGeofence(int geofenceId) { + return native_remove_geofence(geofenceId); + } + + public boolean pauseHardwareGeofence(int geofenceId) { + return native_pause_geofence(geofenceId); + } + + public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) { + return native_resume_geofence(geofenceId, monitorTransition); + } + }; + private boolean deleteAidingData(Bundle extras) { int flags; @@ -948,21 +998,44 @@ public class GpsLocationProvider implements LocationProviderInterface { return false; } - private void startNavigating() { + private void startNavigating(boolean singleShot) { if (!mStarted) { - if (DEBUG) Log.d(TAG, "startNavigating"); + if (DEBUG) Log.d(TAG, "startNavigating, singleShot is " + singleShot); mTimeToFirstFix = 0; mLastFixTime = 0; mStarted = true; + mSingleShot = singleShot; mPositionMode = GPS_POSITION_MODE_STANDALONE; if (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0) { - if (hasCapability(GPS_CAPABILITY_MSB)) { + if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) { + mPositionMode = GPS_POSITION_MODE_MS_ASSISTED; + } else if (hasCapability(GPS_CAPABILITY_MSB)) { mPositionMode = GPS_POSITION_MODE_MS_BASED; } } + if (DEBUG) { + String mode; + + switch(mPositionMode) { + case GPS_POSITION_MODE_STANDALONE: + mode = "standalone"; + break; + case GPS_POSITION_MODE_MS_ASSISTED: + mode = "MS_ASSISTED"; + break; + case GPS_POSITION_MODE_MS_BASED: + mode = "MS_BASED"; + break; + default: + mode = "unknown"; + break; + } + Log.d(TAG, "setting position_mode to " + mode); + } + int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000); if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC, interval, 0, 0)) { @@ -994,6 +1067,7 @@ public class GpsLocationProvider implements LocationProviderInterface { if (DEBUG) Log.d(TAG, "stopNavigating"); if (mStarted) { mStarted = false; + mSingleShot = false; native_stop(); mTimeToFirstFix = 0; mLastFixTime = 0; @@ -1017,6 +1091,7 @@ public class GpsLocationProvider implements LocationProviderInterface { return ((mEngineCapabilities & capability) != 0); } + /** * called from native code to update our position. */ @@ -1087,6 +1162,10 @@ public class GpsLocationProvider implements LocationProviderInterface { } } + if (mSingleShot) { + stopNavigating(); + } + if (mStarted && mStatus != LocationProvider.AVAILABLE) { // we want to time out if we do not receive a fix // within the time out and we are requesting infrequent fixes @@ -1248,7 +1327,8 @@ public class GpsLocationProvider implements LocationProviderInterface { if (DEBUG) Log.d(TAG, "PhoneConstants.APN_REQUEST_STARTED"); // Nothing to do here } else { - if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed"); + if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed, value is " + + result); mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED; native_agps_data_conn_failed(); } @@ -1320,6 +1400,73 @@ public class GpsLocationProvider implements LocationProviderInterface { sendMessage(DOWNLOAD_XTRA_DATA, 0, null); } + /** + * Called from native to report GPS Geofence transition + * All geofence callbacks are called on the same thread + */ + private void reportGeofenceTransition(int geofenceId, int flags, double latitude, + double longitude, double altitude, float speed, float bearing, float accuracy, + long timestamp, int transition, long transitionTimestamp) { + if (mGeofenceHardwareImpl == null) { + mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext); + } + mGeofenceHardwareImpl.reportGpsGeofenceTransition(geofenceId, flags, latitude, longitude, + altitude, speed, bearing, accuracy, timestamp, transition, transitionTimestamp); + } + + /** + * called from native code to report GPS status change. + */ + private void reportGeofenceStatus(int status, int flags, double latitude, + double longitude, double altitude, float speed, float bearing, float accuracy, + long timestamp) { + if (mGeofenceHardwareImpl == null) { + mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext); + } + mGeofenceHardwareImpl.reportGpsGeofenceStatus(status, flags, latitude, longitude, altitude, + speed, bearing, accuracy, timestamp); + } + + /** + * called from native code - Geofence Add callback + */ + private void reportGeofenceAddStatus(int geofenceId, int status) { + if (mGeofenceHardwareImpl == null) { + mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext); + } + mGeofenceHardwareImpl.reportGpsGeofenceAddStatus(geofenceId, status); + } + + /** + * called from native code - Geofence Remove callback + */ + private void reportGeofenceRemoveStatus(int geofenceId, int status) { + if (mGeofenceHardwareImpl == null) { + mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext); + } + mGeofenceHardwareImpl.reportGpsGeofenceRemoveStatus(geofenceId, status); + } + + /** + * called from native code - Geofence Pause callback + */ + private void reportGeofencePauseStatus(int geofenceId, int status) { + if (mGeofenceHardwareImpl == null) { + mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext); + } + mGeofenceHardwareImpl.reportGpsGeofencePauseStatus(geofenceId, status); + } + + /** + * called from native code - Geofence Resume callback + */ + private void reportGeofenceResumeStatus(int geofenceId, int status) { + if (mGeofenceHardwareImpl == null) { + mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext); + } + mGeofenceHardwareImpl.reportGpsGeofenceResumeStatus(geofenceId, status); + } + //============================================================= // NI Client support //============================================================= @@ -1650,4 +1797,13 @@ public class GpsLocationProvider implements LocationProviderInterface { private native void native_update_network_state(boolean connected, int type, boolean roaming, boolean available, String extraInfo, String defaultAPN); + + // Hardware Geofence support. + private static native boolean native_is_geofence_supported(); + private static native boolean native_add_geofence(int geofenceId, double latitude, + double longitude, double radius, int lastTransition,int monitorTransitions, + int notificationResponsivenes, int unknownTimer); + private static native boolean native_remove_geofence(int geofenceId); + private static native boolean native_resume_geofence(int geofenceId, int transitions); + private static native boolean native_pause_geofence(int geofenceId); } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index cc9b785..1b8ee82 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -8249,6 +8249,24 @@ public class PackageManagerService extends IPackageManager.Stub { updatePermissionsLPw(newPackage.packageName, newPackage, UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0)); + // For system-bundled packages, we assume that installing an upgraded version + // of the package implies that the user actually wants to run that new code, + // so we enable the package. + if (isSystemApp(newPackage)) { + // NB: implicit assumption that system package upgrades apply to all users + if (DEBUG_INSTALL) { + Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName); + } + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + if (res.origUsers != null) { + for (int userHandle : res.origUsers) { + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, + userHandle, installerPackageName); + } + } + } + } res.name = pkgName; res.uid = newPackage.applicationInfo.uid; res.pkg = newPackage; @@ -8676,15 +8694,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (outInfo != null) { // A user ID was deleted here. Go through all users and remove it // from KeyStore. - final int appId = outInfo.removedAppId; - if (appId != -1) { - final KeyStore keyStore = KeyStore.getInstance(); - if (keyStore != null) { - for (final int userId : sUserManager.getUserIds()) { - keyStore.clearUid(UserHandle.getUid(userId, appId)); - } - } - } + removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId); } } @@ -8844,6 +8854,7 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.removedUsers = new int[] {removeUser}; } mInstaller.clearUserData(packageName, removeUser); + removeKeystoreDataIfNeeded(removeUser, appId); schedulePackageCleaning(packageName, removeUser, false); return true; } @@ -8996,29 +9007,34 @@ public class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package p; boolean dataOnly = false; + final int appId; synchronized (mPackages) { p = mPackages.get(packageName); - if(p == null) { + if (p == null) { dataOnly = true; PackageSetting ps = mSettings.mPackages.get(packageName); - if((ps == null) || (ps.pkg == null)) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); + if ((ps == null) || (ps.pkg == null)) { + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); return false; } p = ps.pkg; } - } - - if (!dataOnly) { - //need to check this only for fully installed applications - if (p == null) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); - return false; + if (!dataOnly) { + // need to check this only for fully installed applications + if (p == null) { + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); + return false; + } + final ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); + return false; + } } - final ApplicationInfo applicationInfo = p.applicationInfo; - if (applicationInfo == null) { - Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); - return false; + if (p != null && p.applicationInfo != null) { + appId = p.applicationInfo.uid; + } else { + appId = -1; } } int retCode = mInstaller.clearUserData(packageName, userId); @@ -9027,9 +9043,33 @@ public class PackageManagerService extends IPackageManager.Stub { + packageName); return false; } + removeKeystoreDataIfNeeded(userId, appId); return true; } + /** + * Remove entries from the keystore daemon. Will only remove it if the + * {@code appId} is valid. + */ + private static void removeKeystoreDataIfNeeded(int userId, int appId) { + if (appId < 0) { + return; + } + + final KeyStore keyStore = KeyStore.getInstance(); + if (keyStore != null) { + if (userId == UserHandle.USER_ALL) { + for (final int individual : sUserManager.getUserIds()) { + keyStore.clearUid(UserHandle.getUid(individual, appId)); + } + } else { + keyStore.clearUid(UserHandle.getUid(userId, appId)); + } + } else { + Slog.w(TAG, "Could not contact keystore to clear entries for app id " + appId); + } + } + public void deleteApplicationCacheFiles(final String packageName, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index aa1b2ff..df90a56 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -622,6 +622,8 @@ public class UserManagerService extends IUserManager.Stub { UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER); serializer.endTag(null, TAG_RESTRICTIONS); } serializer.endTag(null, TAG_USER); @@ -742,6 +744,8 @@ public class UserManagerService extends IUserManager.Stub { UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); + readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER); } } } @@ -963,7 +967,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) { if (UserHandle.getCallingUserId() != userId - || Binder.getCallingUid() != getUidForPackage(packageName)) { + || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) { checkManageUsersPermission("Only system can get restrictions for other users/apps"); } synchronized (mPackagesLock) { @@ -976,7 +980,7 @@ public class UserManagerService extends IUserManager.Stub { public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries, int userId) { if (UserHandle.getCallingUserId() != userId - || Binder.getCallingUid() != getUidForPackage(packageName)) { + || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) { checkManageUsersPermission("Only system can set restrictions for other users/apps"); } synchronized (mPackagesLock) { @@ -986,11 +990,14 @@ public class UserManagerService extends IUserManager.Stub { } private int getUidForPackage(String packageName) { + long ident = Binder.clearCallingIdentity(); try { return mContext.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES).uid; } catch (NameNotFoundException nnfe) { return -1; + } finally { + Binder.restoreCallingIdentity(ident); } } diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 2652739..1203e02 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -432,7 +432,7 @@ public final class PowerManagerService extends IPowerManager.Stub mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting(); - SensorManager sensorManager = new SystemSensorManager(mHandler.getLooper()); + SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); // The notifier runs on the system server's main looper so as not to interfere // with the animations and other critical functions of the power manager. diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java index c94f7c1..9601e9a 100644 --- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java +++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java @@ -56,9 +56,9 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate"; - private final File updateDir; - private final File updateContent; - private final File updateVersion; + protected final File updateDir; + protected final File updateContent; + protected final File updateVersion; public ConfigUpdateInstallReceiver(String updateDir, String updateContentPath, String updateMetadataPath, String updateVersionPath) { @@ -222,7 +222,7 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT)); } - private void writeUpdate(File dir, File file, byte[] content) throws IOException { + protected void writeUpdate(File dir, File file, byte[] content) throws IOException { FileOutputStream out = null; File tmp = null; try { diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java index 748849e..e8337f6 100644 --- a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java +++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java @@ -18,28 +18,127 @@ package com.android.server.updates; import android.content.Context; import android.content.Intent; +import android.os.FileUtils; import android.os.SELinux; +import android.os.SystemProperties; import android.provider.Settings; import android.util.Base64; import android.util.Slog; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import libcore.io.ErrnoException; +import libcore.io.IoUtils; +import libcore.io.Libcore; + public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { + private static final String TAG = "SELinuxPolicyInstallReceiver"; + + private static final String sepolicyPath = "sepolicy"; + private static final String fileContextsPath = "file_contexts"; + private static final String propertyContextsPath = "property_contexts"; + private static final String seappContextsPath = "seapp_contexts"; + public SELinuxPolicyInstallReceiver() { - super("/data/security/", "sepolicy", "metadata/", "version"); + super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version"); } - @Override - protected void install(byte[] encodedContent, int version) throws IOException { - super.install(Base64.decode(encodedContent, Base64.DEFAULT), version); + private void backupContexts(File contexts) { + new File(contexts, seappContextsPath).renameTo( + new File(contexts, seappContextsPath + "_backup")); + + new File(contexts, propertyContextsPath).renameTo( + new File(contexts, propertyContextsPath + "_backup")); + + new File(contexts, fileContextsPath).renameTo( + new File(contexts, fileContextsPath + "_backup")); + + new File(contexts, sepolicyPath).renameTo( + new File(contexts, sepolicyPath + "_backup")); + } + + private void copyUpdate(File contexts) { + new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath)); + new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath)); + new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath)); + new File(updateDir, sepolicyPath).renameTo(new File(contexts, sepolicyPath)); + } + + private int readInt(BufferedInputStream reader) throws IOException { + int value = 0; + for (int i=0; i < 4; i++) { + value = (value << 8) | reader.read(); + } + return value; + } + + private int[] readChunkLengths(BufferedInputStream bundle) throws IOException { + int[] chunks = new int[4]; + chunks[0] = readInt(bundle); + chunks[1] = readInt(bundle); + chunks[2] = readInt(bundle); + chunks[3] = readInt(bundle); + return chunks; + } + + private void installFile(File destination, BufferedInputStream stream, int length) + throws IOException { + byte[] chunk = new byte[length]; + stream.read(chunk, 0, length); + writeUpdate(updateDir, destination, Base64.decode(chunk, Base64.DEFAULT)); + } + + private void unpackBundle() throws IOException { + BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent)); + int[] chunkLengths = readChunkLengths(stream); + installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]); + installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]); + installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]); + installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]); + } + + private void applyUpdate() throws IOException, ErrnoException { + Slog.i(TAG, "Applying SELinux policy"); + File contexts = new File(updateDir.getParentFile(), "contexts"); + File current = new File(updateDir.getParentFile(), "current"); + File update = new File(updateDir.getParentFile(), "update"); + File tmp = new File(updateDir.getParentFile(), "tmp"); + if (current.exists()) { + Libcore.os.symlink(updateDir.getPath(), update.getPath()); + Libcore.os.rename(update.getPath(), current.getPath()); + } else { + Libcore.os.symlink(updateDir.getPath(), current.getPath()); + } + contexts.mkdirs(); + backupContexts(contexts); + copyUpdate(contexts); + Libcore.os.symlink(contexts.getPath(), tmp.getPath()); + Libcore.os.rename(tmp.getPath(), current.getPath()); + SystemProperties.set("selinux.reload_policy", "1"); + } + + private void setEnforcingMode(Context context) { + boolean mode = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.SELINUX_STATUS, 0) == 1; + SELinux.setSELinuxEnforce(mode); } @Override protected void postInstall(Context context, Intent intent) { - boolean mode = Settings.Global.getInt(context.getContentResolver(), - Settings.Global.SELINUX_STATUS, 0) == 1; - SELinux.setSELinuxEnforce(mode); + try { + unpackBundle(); + applyUpdate(); + setEnforcingMode(context); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "SELinux policy update malformed: ", e); + } catch (IOException e) { + Slog.e(TAG, "Could not update selinux policy: ", e); + } catch (ErrnoException e) { + Slog.e(TAG, "Could not update selinux policy: ", e); + } } } diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index f8d5d2e..4d23e5c 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -734,7 +734,7 @@ public final class WifiService extends IWifiManager.Stub { if (gateway instanceof Inet4Address) { info.gateway = NetworkUtils.inetAddressToInt((Inet4Address)gateway); } - } else if (r.isHostRoute()) { + } else if (r.hasGateway() == false) { LinkAddress dest = r.getDestination(); if (dest.getAddress() instanceof Inet4Address) { info.netmask = NetworkUtils.prefixLengthToNetmaskInt( diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java index 297324b..6293dc6 100644 --- a/services/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/java/com/android/server/wm/AppWindowAnimator.java @@ -4,6 +4,7 @@ package com.android.server.wm; import android.graphics.Matrix; import android.util.Slog; +import android.util.TimeUtils; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; @@ -30,6 +31,11 @@ public class AppWindowAnimator { // Protect with mAnimator. boolean freezingScreen; + /** + * How long we last kept the screen frozen. + */ + int lastFreezeDuration; + // Offset to the window of all layers in the token, for use by // AppWindowToken animations. int animLayerAdjustment; @@ -287,6 +293,10 @@ public class AppWindowAnimator { pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen); pw.print(" allDrawn="); pw.print(allDrawn); pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment); + if (lastFreezeDuration != 0) { + pw.print(prefix); pw.print("lastFreezeDuration="); + TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println(); + } if (animating || animation != null) { pw.print(prefix); pw.print("animating="); pw.println(animating); pw.print(prefix); pw.print("animation="); pw.println(animation); diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java index 3964782..054a075 100644 --- a/services/java/com/android/server/wm/WindowAnimator.java +++ b/services/java/com/android/server/wm/WindowAnimator.java @@ -70,6 +70,7 @@ public class WindowAnimator { int mAboveUniverseLayer = 0; int mBulkUpdateParams = 0; + Object mLastWindowFreezeSource; SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<WindowAnimator.DisplayContentsAnimator>(); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index af603fd..1d1fda5 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -43,6 +43,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import android.app.AppOpsManager; +import android.util.TimeUtils; import android.view.IWindowId; import com.android.internal.app.IBatteryStats; import com.android.internal.policy.PolicyManager; @@ -464,6 +465,9 @@ public class WindowManagerService extends IWindowManager.Stub boolean mTraversalScheduled = false; boolean mDisplayFrozen = false; + long mDisplayFreezeTime = 0; + int mLastDisplayFreezeDuration = 0; + Object mLastFinishedFreezeSource = null; boolean mWaitingForConfig = false; boolean mWindowsFreezingScreen = false; boolean mClientFreezingScreen = false; @@ -582,6 +586,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mWallpaperForceHidingChanged = false; boolean mWallpaperMayChange = false; boolean mOrientationChangeComplete = true; + Object mLastWindowFreezeSource = null; private Session mHoldScreen = null; private boolean mObscured = false; boolean mDimming = false; @@ -3590,7 +3595,10 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { mCurConfiguration = new Configuration(config); - mWaitingForConfig = false; + if (mWaitingForConfig) { + mWaitingForConfig = false; + mLastFinishedFreezeSource = "new-config"; + } performLayoutAndPlaceSurfacesLocked(); } } @@ -4209,6 +4217,7 @@ public class WindowManagerService extends IWindowManager.Stub w.mOrientationChanging = true; mInnerFields.mOrientationChangeComplete = false; } + w.mLastFreezeDuration = 0; unfrozeWindows = true; w.mDisplayContent.layoutNeeded = true; } @@ -4216,7 +4225,10 @@ public class WindowManagerService extends IWindowManager.Stub if (force || unfrozeWindows) { if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken); wtoken.mAppAnimator.freezingScreen = false; + wtoken.mAppAnimator.lastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); mAppsFreezingScreen--; + mLastFinishedFreezeSource = wtoken; } if (unfreezeSurfaceNow) { if (unfrozeWindows) { @@ -4242,6 +4254,7 @@ public class WindowManagerService extends IWindowManager.Stub if (!wtoken.hiddenRequested) { if (!wtoken.mAppAnimator.freezingScreen) { wtoken.mAppAnimator.freezingScreen = true; + wtoken.mAppAnimator.lastFreezeDuration = 0; mAppsFreezingScreen++; if (mAppsFreezingScreen == 1) { startFreezingDisplayLocked(false, 0, 0); @@ -4750,6 +4763,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { if (mClientFreezingScreen) { mClientFreezingScreen = false; + mLastFinishedFreezeSource = "client"; final long origId = Binder.clearCallingIdentity(); try { stopFreezingDisplayLocked(); @@ -5742,6 +5756,7 @@ public class WindowManagerService extends IWindowManager.Stub w.mOrientationChanging = true; mInnerFields.mOrientationChangeComplete = false; } + w.mLastFreezeDuration = 0; } for (int i=mRotationWatchers.size()-1; i>=0; i--) { @@ -6240,6 +6255,7 @@ public class WindowManagerService extends IWindowManager.Stub if (config == null && mWaitingForConfig) { // Nothing changed but we are waiting for something... stop that! mWaitingForConfig = false; + mLastFinishedFreezeSource = "new-config"; performLayoutAndPlaceSurfacesLocked(); } return config; @@ -7036,6 +7052,8 @@ public class WindowManagerService extends IWindowManager.Stub WindowState w = windows.get(i); if (w.mOrientationChanging) { w.mOrientationChanging = false; + w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); Slog.w(TAG, "Force clearing orientation change: " + w); } } @@ -7112,6 +7130,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { if (mClientFreezingScreen) { mClientFreezingScreen = false; + mLastFinishedFreezeSource = "client-timeout"; stopFreezingDisplayLocked(); } } @@ -8029,6 +8048,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ORIENTATION) Slog.v(TAG, "Changing surface while display frozen: " + w); w.mOrientationChanging = true; + w.mLastFreezeDuration = 0; mInnerFields.mOrientationChangeComplete = false; if (!mWindowsFreezingScreen) { mWindowsFreezingScreen = true; @@ -8417,6 +8437,8 @@ public class WindowManagerService extends IWindowManager.Stub "Orientation not waiting for draw in " + w + ", surface " + winAnimator.mSurfaceControl); w.mOrientationChanging = false; + w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); } } } @@ -8930,6 +8952,8 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.mSurfaceResized = false; } catch (RemoteException e) { win.mOrientationChanging = false; + win.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() + - mDisplayFreezeTime); } mResizingWindows.remove(i); } @@ -8940,6 +8964,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mInnerFields.mOrientationChangeComplete) { if (mWindowsFreezingScreen) { mWindowsFreezingScreen = false; + mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource; mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); } stopFreezingDisplayLocked(); @@ -9226,6 +9251,7 @@ public class WindowManagerService extends IWindowManager.Stub mInnerFields.mOrientationChangeComplete = false; } else { mInnerFields.mOrientationChangeComplete = true; + mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource; if (mWindowsFreezingScreen) { doRequest = true; } @@ -9498,6 +9524,8 @@ public class WindowManagerService extends IWindowManager.Stub mScreenFrozenLock.acquire(); mDisplayFrozen = true; + mDisplayFreezeTime = SystemClock.elapsedRealtime(); + mLastFinishedFreezeSource = null; mInputMonitor.freezeInputDispatchingLw(); @@ -9552,6 +9580,15 @@ public class WindowManagerService extends IWindowManager.Stub } mDisplayFrozen = false; + mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime); + StringBuilder sb = new StringBuilder(128); + sb.append("Screen frozen for "); + TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb); + if (mLastFinishedFreezeSource != null) { + sb.append(" due to "); + sb.append(mLastFinishedFreezeSource); + } + Slog.i(TAG, sb.toString()); mH.removeMessages(H.APP_FREEZE_TIMEOUT); mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT); if (PROFILE_ORIENTATION) { @@ -10076,6 +10113,13 @@ public class WindowManagerService extends IWindowManager.Stub } pw.print(" mInTouchMode="); pw.print(mInTouchMode); pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); + pw.print(" mLastDisplayFreezeDuration="); + TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw); + if ( mLastFinishedFreezeSource != null) { + pw.print(" due to "); + pw.print(mLastFinishedFreezeSource); + } + pw.println(); if (dumpAll) { pw.print(" mSystemDecorRect="); pw.print(mSystemDecorRect.toShortString()); pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer); diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index 506fcec..788d514 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD; @@ -26,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import android.app.AppOpsManager; import android.os.RemoteCallbackList; +import android.util.TimeUtils; import android.view.IWindowFocusObserver; import android.view.IWindowId; import com.android.server.input.InputWindowHandle; @@ -266,6 +268,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { */ boolean mOrientationChanging; + /** + * How long we last kept the screen frozen. + */ + int mLastFreezeDuration; + /** Is this window now (or just being) removed? */ boolean mRemoved; @@ -917,6 +924,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mContentChanged && !mExiting && !mWinAnimator.mLastHidden && mService.okToDisplay() && (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left) + && (mAttrs.privateFlags&PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove()); } @@ -1387,6 +1395,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.print(" mAppFreezing="); pw.print(mAppFreezing); pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen); } + if (mLastFreezeDuration != 0) { + pw.print(prefix); pw.print("mLastFreezeDuration="); + TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println(); + } if (mHScale != 1 || mVScale != 1) { pw.print(prefix); pw.print("mHScale="); pw.print(mHScale); pw.print(" mVScale="); pw.println(mVScale); diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index 3a9f7cb..c07174b 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -505,20 +505,20 @@ class WindowStateAnimator { public void setAlpha(float alpha) { super.setAlpha(alpha); if (alpha != mSurfaceTraceAlpha) { + mSurfaceTraceAlpha = alpha; Slog.v(SURFACE_TAG, "setAlpha: " + this + ". Called by " + Debug.getCallers(3)); } - mSurfaceTraceAlpha = alpha; } @Override public void setLayer(int zorder) { super.setLayer(zorder); if (zorder != mLayer) { + mLayer = zorder; Slog.v(SURFACE_TAG, "setLayer: " + this + ". Called by " + Debug.getCallers(3)); } - mLayer = zorder; sSurfaces.remove(this); int i; @@ -535,20 +535,20 @@ class WindowStateAnimator { public void setPosition(float x, float y) { super.setPosition(x, y); if (x != mPosition.x || y != mPosition.y) { + mPosition.set(x, y); Slog.v(SURFACE_TAG, "setPosition: " + this + ". Called by " + Debug.getCallers(3)); } - mPosition.set(x, y); } @Override public void setSize(int w, int h) { super.setSize(w, h); if (w != mSize.x || h != mSize.y) { + mSize.set(w, h); Slog.v(SURFACE_TAG, "setSize: " + this + ". Called by " + Debug.getCallers(3)); } - mSize.set(w, h); } @Override @@ -556,10 +556,10 @@ class WindowStateAnimator { super.setWindowCrop(crop); if (crop != null) { if (!crop.equals(mWindowCrop)) { + mWindowCrop.set(crop); Slog.v(SURFACE_TAG, "setWindowCrop: " + this + ". Called by " + Debug.getCallers(3)); } - mWindowCrop.set(crop); } } @@ -567,28 +567,28 @@ class WindowStateAnimator { public void setLayerStack(int layerStack) { super.setLayerStack(layerStack); if (layerStack != mLayerStack) { + mLayerStack = layerStack; Slog.v(SURFACE_TAG, "setLayerStack: " + this + ". Called by " + Debug.getCallers(3)); } - mLayerStack = layerStack; } @Override public void hide() { super.hide(); if (mShown) { + mShown = false; Slog.v(SURFACE_TAG, "hide: " + this + ". Called by " + Debug.getCallers(3)); } - mShown = false; } @Override public void show() { super.show(); if (!mShown) { + mShown = true; Slog.v(SURFACE_TAG, "show: " + this + ". Called by " + Debug.getCallers(3)); } - mShown = true; } @Override @@ -1307,6 +1307,7 @@ class WindowStateAnimator { if (w.mOrientationChanging) { if (!w.isDrawnLw()) { mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE; + mAnimator.mLastWindowFreezeSource = w; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation continue waiting for draw in " + w); } else { |