diff options
Diffstat (limited to 'services/java')
40 files changed, 1664 insertions, 373 deletions
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 9e06db8..01625dd 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -321,12 +321,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { // track the current default http proxy - tell the world if we get a new one (real change) private ProxyProperties mDefaultProxy = null; - private Object mDefaultProxyLock = new Object(); + private Object mProxyLock = new Object(); private boolean mDefaultProxyDisabled = false; // track the global proxy. private ProxyProperties mGlobalProxy = null; - private final Object mGlobalProxyLock = new Object(); private SettingsObserver mSettingsObserver; @@ -1471,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())) { @@ -2348,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) { @@ -2394,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; } @@ -3039,14 +3038,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { // so this API change wouldn't have a benifit. It also breaks the passing // of proxy info to all the JVMs. // enforceAccessPermission(); - synchronized (mDefaultProxyLock) { - return mDefaultProxyDisabled ? null : mDefaultProxy; + synchronized (mProxyLock) { + if (mGlobalProxy != null) return mGlobalProxy; + return (mDefaultProxyDisabled ? null : mDefaultProxy); } } public void setGlobalProxy(ProxyProperties proxyProperties) { - enforceChangePermission(); - synchronized (mGlobalProxyLock) { + enforceConnectivityInternalPermission(); + synchronized (mProxyLock) { if (proxyProperties == mGlobalProxy) return; if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return; if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return; @@ -3063,16 +3063,21 @@ 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) { proxyProperties = mDefaultProxy; } - //sendProxyBroadcast(proxyProperties); + sendProxyBroadcast(proxyProperties); } private void loadGlobalProxy() { @@ -3083,7 +3088,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST); if (!TextUtils.isEmpty(host)) { ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList); - synchronized (mGlobalProxyLock) { + synchronized (mProxyLock) { mGlobalProxy = proxyProperties; } } @@ -3094,7 +3099,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // so this API change wouldn't have a benifit. It also breaks the passing // of proxy info to all the JVMs. // enforceAccessPermission(); - synchronized (mGlobalProxyLock) { + synchronized (mProxyLock) { return mGlobalProxy; } } @@ -3103,11 +3108,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (proxy != null && TextUtils.isEmpty(proxy.getHost())) { proxy = null; } - synchronized (mDefaultProxyLock) { + synchronized (mProxyLock) { if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return; - if (mDefaultProxy == proxy) return; + if (mDefaultProxy == proxy) return; // catches repeated nulls mDefaultProxy = proxy; + if (mGlobalProxy != null) return; if (!mDefaultProxyDisabled) { sendProxyBroadcast(proxy); } @@ -3350,10 +3356,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { mDnsOverridden = true; } - // Temporarily disable the default proxy. - synchronized (mDefaultProxyLock) { + // Temporarily disable the default proxy (not global). + synchronized (mProxyLock) { mDefaultProxyDisabled = true; - if (mDefaultProxy != null) { + if (mGlobalProxy == null && mDefaultProxy != null) { sendProxyBroadcast(null); } } @@ -3368,9 +3374,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { mHandler.sendEmptyMessage(EVENT_RESTORE_DNS); } } - synchronized (mDefaultProxyLock) { + synchronized (mProxyLock) { mDefaultProxyDisabled = false; - if (mDefaultProxy != null) { + if (mGlobalProxy == null && mDefaultProxy != null) { sendProxyBroadcast(mDefaultProxy); } } diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags index 8bc2da2..59577ad 100644 --- a/services/java/com/android/server/EventLogTags.logtags +++ b/services/java/com/android/server/EventLogTags.logtags @@ -157,3 +157,8 @@ option java_package com.android.server # ConfigUpdateInstallReceiver.java # --------------------------- 51300 config_install_failed (dir|3) + +# --------------------------- +# IntentFirewall.java +# --------------------------- +51400 ifw_intent_matched (Intent Type|1|5),(Component Name|3),(Caller Uid|1|5),(Caller Pkg Count|1|1),(Caller Pkgs|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 3b541ec..a28c387 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1070,7 +1070,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(getAppShowFlags(), null); } - return new InputBindResult(session.session, session.channel, mCurId, mCurSeq); + return new InputBindResult(session.session, + session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq); } InputBindResult startInputLocked(IInputMethodClient client, @@ -2357,13 +2358,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return true; case MSG_CREATE_SESSION: { args = (SomeArgs)msg.obj; + IInputMethod method = (IInputMethod)args.arg1; InputChannel channel = (InputChannel)args.arg2; try { - ((IInputMethod)args.arg1).createSession(channel, - (IInputSessionCallback)args.arg3); + method.createSession(channel, (IInputSessionCallback)args.arg3); } catch (RemoteException e) { } finally { - if (channel != null) { + // Dispose the channel if the input method is not local to this process + // because the remote proxy will get its own copy when unparceled. + if (channel != null && Binder.isProxy(method)) { channel.dispose(); } } @@ -2404,16 +2407,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // There is nothing interesting about the last client dying. } return true; - case MSG_BIND_METHOD: + case MSG_BIND_METHOD: { args = (SomeArgs)msg.obj; + IInputMethodClient client = (IInputMethodClient)args.arg1; + InputBindResult res = (InputBindResult)args.arg2; try { - ((IInputMethodClient)args.arg1).onBindMethod( - (InputBindResult)args.arg2); + client.onBindMethod(res); } catch (RemoteException e) { Slog.w(TAG, "Client died receiving input method " + args.arg2); + } finally { + // Dispose the channel if the input method is not local to this process + // because the remote proxy will get its own copy when unparceled. + if (res.channel != null && Binder.isProxy(client)) { + res.channel.dispose(); + } } args.recycle(); return true; + } case MSG_SET_ACTIVE: try { ((ClientState)msg.obj).client.setActive(msg.arg1 != 0); 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 44d730c..cfb892f 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -26,16 +26,17 @@ import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.INotificationManager; -import android.app.INotificationListener; import android.app.ITransientNotification; import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -57,6 +58,9 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; +import android.service.notification.INotificationListener; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.AtomicFile; @@ -68,8 +72,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; -import com.android.internal.statusbar.StatusBarNotification; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -121,6 +123,8 @@ public class NotificationManagerService extends INotificationManager.Stub private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; private static final boolean ENABLE_BLOCKED_TOASTS = true; + private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":"; + final Context mContext; final IActivityManager mAm; final UserManager mUserManager; @@ -163,8 +167,18 @@ public class NotificationManagerService extends INotificationManager.Stub private final AppOpsManager mAppOps; - private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>(); - private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>(); + // contains connections to all connected listeners, including app services + // and system listeners + private ArrayList<NotificationListenerInfo> mListeners + = new ArrayList<NotificationListenerInfo>(); + // things that will be put into mListeners as soon as they're ready + private ArrayList<String> mServicesBinding = new ArrayList<String>(); + // lists the component names of all enabled (and therefore connected) listener + // app services for the current user only + private HashSet<ComponentName> mEnabledListenersForCurrentUser + = new HashSet<ComponentName>(); + // Just the packages from mEnabledListenersForCurrentUser + private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>(); // Notification control database. For now just contains disabled packages. private AtomicFile mPolicyFile; @@ -181,27 +195,42 @@ public class NotificationManagerService extends INotificationManager.Stub private class NotificationListenerInfo implements DeathRecipient { INotificationListener listener; - String pkg; + ComponentName component; int userid; boolean isSystem; + ServiceConnection connection; - public NotificationListenerInfo(INotificationListener listener, String pkg, int userid, - boolean isSystem) { + public NotificationListenerInfo(INotificationListener listener, ComponentName component, + int userid, boolean isSystem) { this.listener = listener; - this.pkg = pkg; + this.component = component; this.userid = userid; this.isSystem = isSystem; + this.connection = null; + } + + public NotificationListenerInfo(INotificationListener listener, ComponentName component, + int userid, ServiceConnection connection) { + this.listener = listener; + this.component = component; + this.userid = userid; + this.isSystem = false; + this.connection = connection; } boolean enabledAndUserMatches(StatusBarNotification sbn) { final int nid = sbn.getUserId(); - if (!(isSystem || isEnabledForUser(nid))) return false; + if (!isEnabledForCurrentUser()) { + return false; + } if (this.userid == UserHandle.USER_ALL) return true; return (nid == UserHandle.USER_ALL || nid == this.userid); } public void notifyPostedIfUserMatch(StatusBarNotification sbn) { - if (!enabledAndUserMatches(sbn)) return; + if (!enabledAndUserMatches(sbn)) { + return; + } try { listener.onNotificationPosted(sbn); } catch (RemoteException ex) { @@ -220,15 +249,17 @@ public class NotificationManagerService extends INotificationManager.Stub @Override public void binderDied() { - unregisterListener(this.listener, this.userid); + if (connection == null) { + // This is not a service; it won't be recreated. We can give up this connection. + unregisterListener(this.listener, this.userid); + } } /** convenience method for looking in mEnabledListenersForCurrentUser */ - public boolean isEnabledForUser(int userid) { - for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) { - if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true; - } - return false; + public boolean isEnabledForCurrentUser() { + if (this.isSystem) return true; + if (this.connection == null) return false; + return mEnabledListenersForCurrentUser.contains(this.component); } } @@ -434,6 +465,12 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * System-only API for getting a list of current (i.e. not cleared) notifications. + * + * Requires ACCESS_NOTIFICATIONS which is signature|system. + */ + @Override public StatusBarNotification[] getActiveNotifications(String callingPkg) { // enforce() will ensure the calling uid has the correct permission mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -456,6 +493,12 @@ public class NotificationManagerService extends INotificationManager.Stub return tmp; } + /** + * System-only API for getting a list of recent (cleared, no longer shown) notifications. + * + * Requires ACCESS_NOTIFICATIONS which is signature|system. + */ + @Override public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { // enforce() will ensure the calling uid has the correct permission mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -474,27 +517,76 @@ public class NotificationManagerService extends INotificationManager.Stub return tmp; } - boolean packageCanTapNotificationsForUser(final int uid, final String pkg) { - // Make sure the package and uid match, and that the package is allowed access - return (AppOpsManager.MODE_ALLOWED - == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg)); + /** + * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS + * is altered. (For example in response to USER_SWITCHED in our broadcast receiver) + */ + void rebindListenerServices() { + String flat = Settings.Secure.getString( + mContext.getContentResolver(), + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + + NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()]; + final ArrayList<ComponentName> toAdd; + final int currentUser = ActivityManager.getCurrentUser(); + + synchronized (mNotificationList) { + // unbind and remove all existing listeners + toRemove = mListeners.toArray(toRemove); + + toAdd = new ArrayList<ComponentName>(); + final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>(); + final HashSet<String> newPackages = new HashSet<String>(); + + // decode the list of components + if (flat != null) { + String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR); + for (int i=0; i<components.length; i++) { + final ComponentName component + = ComponentName.unflattenFromString(components[i]); + if (component != null) { + newEnabled.add(component); + toAdd.add(component); + newPackages.add(component.getPackageName()); + } + } + + mEnabledListenersForCurrentUser = newEnabled; + mEnabledListenerPackageNames = newPackages; + } + } + + for (NotificationListenerInfo info : toRemove) { + final ComponentName component = info.component; + final int oldUser = info.userid; + Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component); + unregisterListenerService(component, info.userid); + } + + final int N = toAdd.size(); + for (int i=0; i<N; i++) { + final ComponentName component = toAdd.get(i); + Slog.v(TAG, "enabling notification listener for user " + currentUser + ": " + + component); + registerListenerService(component, currentUser); + } } + /** + * Register a listener binder directly with the notification manager. + * + * Only works with system callers. Apps should extend + * {@link android.service.notification.NotificationListenerService}. + */ @Override public void registerListener(final INotificationListener listener, - final String pkg, final int userid) { - // ensure system or allowed pkg - int uid = Binder.getCallingUid(); - boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0); - if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) { - throw new SecurityException("Package " + pkg - + " may not listen for notifications"); - } + final ComponentName component, final int userid) { + checkCallerIsSystem(); synchronized (mNotificationList) { try { NotificationListenerInfo info - = new NotificationListenerInfo(listener, pkg, userid, isSystem); + = new NotificationListenerInfo(listener, component, userid, true); listener.asBinder().linkToDeath(info, 0); mListeners.add(info); } catch (RemoteException e) { @@ -503,6 +595,90 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * Version of registerListener that takes the name of a + * {@link android.service.notification.NotificationListenerService} to bind to. + * + * This is the mechanism by which third parties may subscribe to notifications. + */ + private void registerListenerService(final ComponentName name, final int userid) { + checkCallerIsSystem(); + + if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid); + + synchronized (mNotificationList) { + final String servicesBindingTag = name.toString() + "/" + userid; + if (mServicesBinding.contains(servicesBindingTag)) { + // stop registering this thing already! we're working on it + return; + } + mServicesBinding.add(servicesBindingTag); + + final int N = mListeners.size(); + for (int i=N-1; i>=0; i--) { + final NotificationListenerInfo info = mListeners.get(i); + if (name.equals(info.component) + && info.userid == userid) { + // cut old connections + if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener); + mListeners.remove(i); + if (info.connection != null) { + mContext.unbindService(info.connection); + } + } + } + + Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE); + intent.setComponent(name); + + intent.putExtra(Intent.EXTRA_CLIENT_LABEL, + com.android.internal.R.string.notification_listener_binding_label); + intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( + mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0)); + + try { + if (DBG) Slog.v(TAG, "binding: " + intent); + if (!mContext.bindServiceAsUser(intent, + new ServiceConnection() { + INotificationListener mListener; + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mNotificationList) { + mServicesBinding.remove(servicesBindingTag); + try { + mListener = INotificationListener.Stub.asInterface(service); + NotificationListenerInfo info = new NotificationListenerInfo( + mListener, name, userid, this); + service.linkToDeath(info, 0); + mListeners.add(info); + } catch (RemoteException e) { + // already dead + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Slog.v(TAG, "notification listener connection lost: " + name); + } + }, + Context.BIND_AUTO_CREATE, + new UserHandle(userid))) + { + mServicesBinding.remove(servicesBindingTag); + Slog.w(TAG, "Unable to bind listener service: " + intent); + return; + } + } catch (SecurityException ex) { + Slog.e(TAG, "Unable to bind listener service: " + intent, ex); + return; + } + } + } + + /** + * Remove a listener binder directly + */ @Override public void unregisterListener(INotificationListener listener, int userid) { // no need to check permissions; if your listener binder is in the list, @@ -513,12 +689,39 @@ public class NotificationManagerService extends INotificationManager.Stub for (int i=N-1; i>=0; i--) { final NotificationListenerInfo info = mListeners.get(i); if (info.listener == listener && info.userid == userid) { - mListeners.remove(listener); + mListeners.remove(i); + if (info.connection != null) { + mContext.unbindService(info.connection); + } + } + } + } + } + + /** + * Remove a listener service for the given user by ComponentName + */ + private void unregisterListenerService(ComponentName name, int userid) { + checkCallerIsSystem(); + + synchronized (mNotificationList) { + final int N = mListeners.size(); + for (int i=N-1; i>=0; i--) { + final NotificationListenerInfo info = mListeners.get(i); + if (name.equals(info.component) + && info.userid == userid) { + mListeners.remove(i); + if (info.connection != null) { + mContext.unbindService(info.connection); + } } } } } + /** + * asynchronously notify all listeners about a new notification + */ private void notifyPostedLocked(NotificationRecord n) { final StatusBarNotification sbn = n.sbn; for (final NotificationListenerInfo info : mListeners) { @@ -530,6 +733,9 @@ public class NotificationManagerService extends INotificationManager.Stub } } + /** + * asynchronously notify all listeners about a removed notification + */ private void notifyRemovedLocked(NotificationRecord n) { final StatusBarNotification sbn = n.sbn; for (final NotificationListenerInfo info : mListeners) { @@ -541,6 +747,57 @@ public class NotificationManagerService extends INotificationManager.Stub } } + // -- APIs to support listeners clicking/clearing notifications -- + + private NotificationListenerInfo checkListenerToken(INotificationListener listener) { + final IBinder token = listener.asBinder(); + final int N = mListeners.size(); + for (int i=0; i<N; i++) { + final NotificationListenerInfo info = mListeners.get(i); + if (info.listener.asBinder() == token) return info; + } + throw new SecurityException("Disallowed call from unknown listener: " + listener); + } + + /** + * Allow an INotificationListener to simulate a "clear all" operation. + * + * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} + * + * @param token The binder for the listener, to check that the caller is allowed + */ + public void clearAllNotificationsFromListener(INotificationListener token) { + NotificationListenerInfo info = checkListenerToken(token); + long identity = Binder.clearCallingIdentity(); + try { + cancelAll(info.userid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Allow an INotificationListener to simulate clearing (dismissing) a single notification. + * + * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} + * + * @param token The binder for the listener, to check that the caller is allowed + */ + public void clearNotificationFromListener(INotificationListener token, String pkg, String tag, int id) { + NotificationListenerInfo info = checkListenerToken(token); + long identity = Binder.clearCallingIdentity(); + try { + cancelNotification(pkg, tag, id, 0, + Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, + true, + info.userid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + // -- end of listener APIs -- + public static final class NotificationRecord { final StatusBarNotification sbn; @@ -759,12 +1016,23 @@ public class NotificationManagerService extends INotificationManager.Stub } pkgList = new String[]{pkgName}; } + + boolean anyListenersInvolved = false; if (pkgList != null && (pkgList.length > 0)) { for (String pkgName : pkgList) { cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart, UserHandle.USER_ALL); + if (mEnabledListenerPackageNames.contains(pkgName)) { + anyListenersInvolved = true; + } } } + + if (anyListenersInvolved) { + // make sure we're still bound to any of our + // listeners who may have just upgraded + rebindListenerServices(); + } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { // Keep track of screen on/off state, but do not turn off the notification light // until user passes through the lock screen or views the notification. @@ -795,7 +1063,7 @@ public class NotificationManagerService extends INotificationManager.Stub = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); private final Uri ENABLED_NOTIFICATION_LISTENERS_URI - = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); SettingsObserver(Handler handler) { super(handler); @@ -804,9 +1072,9 @@ public class NotificationManagerService extends INotificationManager.Stub void observe() { ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, - false, this); + false, this, UserHandle.USER_ALL); resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI, - false, this); + false, this, UserHandle.USER_ALL); update(null); } @@ -825,19 +1093,7 @@ public class NotificationManagerService extends INotificationManager.Stub } } if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) { - String pkglist = Settings.Secure.getString( - mContext.getContentResolver(), - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); - mEnabledListenersForCurrentUser.clear(); - if (pkglist != null) { - String[] pkgs = pkglist.split(";"); - for (int i=0; i<pkgs.length; i++) { - final String pkg = pkgs[i]; - if (pkg != null && ! "".equals(pkg)) { - mEnabledListenersForCurrentUser.add(pkgs[i]); - } - } - } + rebindListenerServices(); } } } @@ -956,6 +1212,9 @@ public class NotificationManagerService extends INotificationManager.Stub // no beeping until we're basically done booting mSystemReady = true; + + // make sure our listener services are properly bound + rebindListenerServices(); } // Toasts @@ -1781,16 +2040,17 @@ public class NotificationManagerService extends INotificationManager.Stub pw.println("Current Notification Manager state:"); - pw.print(" Enabled listeners: ["); - for (String pkg : mEnabledListenersForCurrentUser) { - pw.print(" " + pkg); + pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size() + + ") enabled for current user:"); + for (ComponentName cmpt : mEnabledListenersForCurrentUser) { + pw.println(" " + cmpt); } - pw.println(" ]"); - pw.println(" Live listeners:"); + pw.println(" Live listeners (" + mListeners.size() + "):"); for (NotificationListenerInfo info : mListeners) { - pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener - + (info.isSystem?" SYSTEM":"")); + pw.println(" " + info.component + + " (user " + info.userid + "): " + info.listener + + (info.isSystem?" SYSTEM":"")); } int N; diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 1fe98af..c21d8c6 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import android.app.StatusBarManager; +import android.service.notification.StatusBarNotification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,7 +34,6 @@ import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; 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..128a49f 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--) { @@ -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,6 +1758,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } UserState userState = getUserStateLocked(mUserId); + mKeyEventDispatcher.flush(); if (!mIsAutomation) { mContext.unbindService(this); } else { @@ -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(); @@ -2109,6 +2172,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public void binderDied() { synchronized (mLock) { + mKeyEventDispatcher.flush(); UserState userState = getUserStateLocked(mUserId); // The death recipient is unregistered in removeServiceLocked removeServiceLocked(this, userState); @@ -2141,12 +2205,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 +2275,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 +2301,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 +2414,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 7710f13..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!"); @@ -3858,6 +3864,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.userId != userId) { continue; } + if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) { + continue; + } // Package has been specified, we want to hit all processes // that match it. We need to qualify this by the processes // that are running under the specified app and user ID. @@ -4574,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; } } @@ -7431,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); } } @@ -7733,6 +7758,18 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public void killUid(int uid, String reason) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("killUid only available to the system"); + } + synchronized (this) { + killPackageProcessesLocked(null, UserHandle.getAppId(uid), UserHandle.getUserId(uid), + ProcessList.FOREGROUND_APP_ADJ-1, false, true, true, false, + reason != null ? reason : "kill uid"); + } + } + + @Override public boolean killProcessesBelowForeground(String reason) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("killProcessesBelowForeground() only available to system"); @@ -8350,13 +8387,13 @@ public final class ActivityManagerService extends ActivityManagerNative final String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); - handleApplicationCrashInner(r, processName, crashInfo); + handleApplicationCrashInner("crash", r, processName, crashInfo); } /* Native crash reporting uses this inner version because it needs to be somewhat * decoupled from the AM-managed cleanup lifecycle */ - void handleApplicationCrashInner(ProcessRecord r, String processName, + void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName, ApplicationErrorReport.CrashInfo crashInfo) { EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(), UserHandle.getUserId(Binder.getCallingUid()), processName, @@ -8366,7 +8403,7 @@ public final class ActivityManagerService extends ActivityManagerNative crashInfo.throwFileName, crashInfo.throwLineNumber); - addErrorToDropBox("crash", r, processName, null, null, null, null, null, crashInfo); + addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo); crashApplication(r, crashInfo); } @@ -12420,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 3d7da7b..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; @@ -2594,8 +2595,7 @@ final class ActivityStack { } boolean abort = !mService.mIntentFirewall.checkStartActivity(intent, - callerApp==null?null:callerApp.info, callingPackage, callingUid, callingPid, - resolvedType, aInfo); + callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo); if (mMainStack) { if (mService.mController != null) { @@ -4601,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/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java index e83433f..0688c50 100644 --- a/services/java/com/android/server/am/NativeCrashListener.java +++ b/services/java/com/android/server/am/NativeCrashListener.java @@ -82,7 +82,7 @@ class NativeCrashListener extends Thread { ci.stackTrace = mCrashReport; if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()"); - mAm.handleApplicationCrashInner(mApp, mApp.processName, ci); + mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci); if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned"); } catch (Exception e) { Slog.e(TAG, "Unable to report native crash", e); diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 8ff1c7d..fccaab5 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -376,37 +376,37 @@ class ServiceRecord extends Binder { // icon, but this used to be able to slip through, so for // those dirty apps give it the app's icon. foregroundNoti.icon = appInfo.icon; - if (foregroundNoti.contentView == null) { - // In this case the app may not have specified a - // content view... so we'll give them something to show. - CharSequence appName = appInfo.loadLabel( - ams.mContext.getPackageManager()); - if (appName == null) { - appName = appInfo.packageName; - } - Context ctx = null; - try { - ctx = ams.mContext.createPackageContext( - appInfo.packageName, 0); - Intent runningIntent = new Intent( - Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - runningIntent.setData(Uri.fromParts("package", - appInfo.packageName, null)); - PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0, - runningIntent, PendingIntent.FLAG_UPDATE_CURRENT); - foregroundNoti.setLatestEventInfo(ctx, - ams.mContext.getString( - com.android.internal.R.string - .app_running_notification_title, - appName), - ams.mContext.getString( - com.android.internal.R.string - .app_running_notification_text, - appName), - pi); - } catch (PackageManager.NameNotFoundException e) { - foregroundNoti.icon = 0; - } + + // Do not allow apps to present a sneaky invisible content view either. + foregroundNoti.contentView = null; + foregroundNoti.bigContentView = null; + CharSequence appName = appInfo.loadLabel( + ams.mContext.getPackageManager()); + if (appName == null) { + appName = appInfo.packageName; + } + Context ctx = null; + try { + ctx = ams.mContext.createPackageContext( + appInfo.packageName, 0); + Intent runningIntent = new Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + runningIntent.setData(Uri.fromParts("package", + appInfo.packageName, null)); + PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0, + runningIntent, PendingIntent.FLAG_UPDATE_CURRENT); + foregroundNoti.setLatestEventInfo(ctx, + ams.mContext.getString( + com.android.internal.R.string + .app_running_notification_title, + appName), + ams.mContext.getString( + com.android.internal.R.string + .app_running_notification_text, + appName), + pi); + } catch (PackageManager.NameNotFoundException e) { + foregroundNoti.icon = 0; } } if (foregroundNoti.icon == 0) { 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/firewall/AndFilter.java b/services/java/com/android/server/firewall/AndFilter.java index cabf00b..e4276d0 100644 --- a/services/java/com/android/server/firewall/AndFilter.java +++ b/services/java/com/android/server/firewall/AndFilter.java @@ -26,11 +26,10 @@ import java.io.IOException; class AndFilter extends FilterList { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { for (int i=0; i<children.size(); i++) { - if (!children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid, - callerPid, resolvedType, resolvedApp)) { + if (!children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType, + resolvedApp)) { return false; } } diff --git a/services/java/com/android/server/firewall/CategoryFilter.java b/services/java/com/android/server/firewall/CategoryFilter.java index d5e9fe8..4938cb8 100644 --- a/services/java/com/android/server/firewall/CategoryFilter.java +++ b/services/java/com/android/server/firewall/CategoryFilter.java @@ -34,7 +34,7 @@ class CategoryFilter implements Filter { } @Override - public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage, + public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { Set<String> categories = intent.getCategories(); if (categories == null) { diff --git a/services/java/com/android/server/firewall/Filter.java b/services/java/com/android/server/firewall/Filter.java index 7639466..0e783e8 100644 --- a/services/java/com/android/server/firewall/Filter.java +++ b/services/java/com/android/server/firewall/Filter.java @@ -26,17 +26,14 @@ interface Filter { * @param ifw The IntentFirewall instance * @param intent The intent being started/bound/broadcast * @param callerApp An ApplicationInfo of an application in the caller's process. This may not - * be the specific app that is actually sending the intent. This also may be - * null, if the caller is the system process, or an unrecognized process (e.g. - * am start) - * @param callerPackage The package name of the component sending the intent. This value is -* provided by the caller and might be forged/faked. + * be the specific app that is actually sending the intent. This also may be + * null, if the caller is the system process, or an unrecognized process (e.g. + * am start) * @param callerUid * @param callerPid * @param resolvedType The resolved mime type of the intent * @param resolvedApp The application that contains the resolved component that the intent is */ boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp); + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp); } diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java index ebbbd86..08e6b45 100644 --- a/services/java/com/android/server/firewall/IntentFirewall.java +++ b/services/java/com/android/server/firewall/IntentFirewall.java @@ -16,18 +16,21 @@ package com.android.server.firewall; +import android.app.AppGlobals; +import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.Environment; -import android.os.ServiceManager; +import android.os.RemoteException; import android.util.Slog; import android.util.Xml; import com.android.internal.util.XmlUtils; +import com.android.server.EventLogTags; import com.android.server.IntentResolver; -import com.android.server.pm.PackageManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -42,13 +45,22 @@ import java.util.List; public class IntentFirewall { private static final String TAG = "IntentFirewall"; - private static final String RULES_FILENAME = "ifw.xml"; + // e.g. /data/system/ifw/ifw.xml or /data/secure/system/ifw/ifw.xml + private static final File RULES_FILE = + new File(Environment.getSystemSecureDirectory(), "ifw/ifw.xml"); + + private static final int LOG_PACKAGES_MAX_LENGTH = 150; + private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125; private static final String TAG_RULES = "rules"; private static final String TAG_ACTIVITY = "activity"; private static final String TAG_SERVICE = "service"; private static final String TAG_BROADCAST = "broadcast"; + private static final int TYPE_ACTIVITY = 0; + private static final int TYPE_SERVICE = 1; + private static final int TYPE_BROADCAST = 2; + private static final HashMap<String, FilterFactory> factoryMap; private final AMSInterface mAms; @@ -74,7 +86,6 @@ public class IntentFirewall { StringFilter.HOST, StringFilter.MIME_TYPE, StringFilter.PATH, - StringFilter.SENDER_PACKAGE, StringFilter.SSP, CategoryFilter.FACTORY, @@ -93,22 +104,19 @@ public class IntentFirewall { public IntentFirewall(AMSInterface ams) { mAms = ams; - File dataSystemDir = new File(Environment.getDataDirectory(), "system"); - File rulesFile = new File(dataSystemDir, RULES_FILENAME); - readRules(rulesFile); + readRules(getRulesFile()); } - public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ActivityInfo resolvedActivity) { + public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp, int callerUid, + int callerPid, String resolvedType, ActivityInfo resolvedActivity) { List<Rule> matchingRules = mActivityResolver.queryIntent(intent, resolvedType, false, 0); boolean log = false; boolean block = false; for (int i=0; i< matchingRules.size(); i++) { Rule rule = matchingRules.get(i); - if (rule.matches(this, intent, callerApp, callerPackage, callerUid, callerPid, - resolvedType, resolvedActivity.applicationInfo)) { + if (rule.matches(this, intent, callerApp, callerUid, callerPid, resolvedType, + resolvedActivity.applicationInfo)) { block |= rule.getBlock(); log |= rule.getLog(); @@ -121,12 +129,85 @@ public class IntentFirewall { } if (log) { - // TODO: log info about intent to event log + logIntent(TYPE_ACTIVITY, intent, callerUid, resolvedType); } return !block; } + private static void logIntent(int intentType, Intent intent, int callerUid, + String resolvedType) { + // The component shouldn't be null, but let's double check just to be safe + ComponentName cn = intent.getComponent(); + String shortComponent = null; + if (cn != null) { + shortComponent = cn.flattenToShortString(); + } + + String callerPackages = null; + int callerPackageCount = 0; + IPackageManager pm = AppGlobals.getPackageManager(); + if (pm != null) { + try { + String[] callerPackagesArray = pm.getPackagesForUid(callerUid); + if (callerPackagesArray != null) { + callerPackageCount = callerPackagesArray.length; + callerPackages = joinPackages(callerPackagesArray); + } + } catch (RemoteException ex) { + Slog.e(TAG, "Remote exception while retrieving packages", ex); + } + } + + EventLogTags.writeIfwIntentMatched(intentType, shortComponent, callerUid, + callerPackageCount, callerPackages, intent.getAction(), resolvedType, + intent.getDataString(), intent.getFlags()); + } + + /** + * Joins a list of package names such that the resulting string is no more than + * LOG_PACKAGES_MAX_LENGTH. + * + * Only full package names will be added to the result, unless every package is longer than the + * limit, in which case one of the packages will be truncated and added. In this case, an + * additional '-' character will be added to the end of the string, to denote the truncation. + * + * If it encounters a package that won't fit in the remaining space, it will continue on to the + * next package, unless the total length of the built string so far is greater than + * LOG_PACKAGES_SUFFICIENT_LENGTH, in which case it will stop and return what it has. + */ + private static String joinPackages(String[] packages) { + boolean first = true; + StringBuilder sb = new StringBuilder(); + for (int i=0; i<packages.length; i++) { + String pkg = packages[i]; + + // + 1 length for the comma. This logic technically isn't correct for the first entry, + // but it's not critical. + if (sb.length() + pkg.length() + 1 < LOG_PACKAGES_MAX_LENGTH) { + if (!first) { + sb.append(','); + } else { + first = false; + } + sb.append(pkg); + } else if (sb.length() >= LOG_PACKAGES_SUFFICIENT_LENGTH) { + return sb.toString(); + } + } + if (sb.length() == 0 && packages.length > 0) { + String pkg = packages[0]; + // truncating from the end - the last part of the package name is more likely to be + // interesting/unique + return pkg.substring(pkg.length() - LOG_PACKAGES_MAX_LENGTH + 1) + '-'; + } + return null; + } + + public static File getRulesFile() { + return RULES_FILE; + } + private void readRules(File rulesFile) { FileInputStream fis; try { @@ -309,7 +390,12 @@ public class IntentFirewall { } boolean signaturesMatch(int uid1, int uid2) { - PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); - return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH; + try { + IPackageManager pm = AppGlobals.getPackageManager(); + return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH; + } catch (RemoteException ex) { + Slog.e(TAG, "Remote exception while checking signatures", ex); + return false; + } } } diff --git a/services/java/com/android/server/firewall/NotFilter.java b/services/java/com/android/server/firewall/NotFilter.java index 2ff108a..f0fc337 100644 --- a/services/java/com/android/server/firewall/NotFilter.java +++ b/services/java/com/android/server/firewall/NotFilter.java @@ -33,10 +33,9 @@ class NotFilter implements Filter { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { - return !mChild.matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid, - resolvedType, resolvedApp); + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { + return !mChild.matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType, + resolvedApp); } public static final FilterFactory FACTORY = new FilterFactory("not") { diff --git a/services/java/com/android/server/firewall/OrFilter.java b/services/java/com/android/server/firewall/OrFilter.java index 1ed1c85..72db31e 100644 --- a/services/java/com/android/server/firewall/OrFilter.java +++ b/services/java/com/android/server/firewall/OrFilter.java @@ -26,11 +26,10 @@ import java.io.IOException; class OrFilter extends FilterList { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { for (int i=0; i<children.size(); i++) { - if (children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid, - resolvedType, resolvedApp)) { + if (children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType, + resolvedApp)) { return true; } } diff --git a/services/java/com/android/server/firewall/PortFilter.java b/services/java/com/android/server/firewall/PortFilter.java index 2b2a198..fe7e085 100644 --- a/services/java/com/android/server/firewall/PortFilter.java +++ b/services/java/com/android/server/firewall/PortFilter.java @@ -42,8 +42,7 @@ class PortFilter implements Filter { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { int port = -1; Uri uri = intent.getData(); if (uri != null) { diff --git a/services/java/com/android/server/firewall/SenderFilter.java b/services/java/com/android/server/firewall/SenderFilter.java index 0b790bd..58bdd73 100644 --- a/services/java/com/android/server/firewall/SenderFilter.java +++ b/services/java/com/android/server/firewall/SenderFilter.java @@ -68,8 +68,7 @@ class SenderFilter { private static final Filter SIGNATURE = new Filter() { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { if (callerApp == null) { return false; } @@ -80,8 +79,7 @@ class SenderFilter { private static final Filter SYSTEM = new Filter() { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { if (callerApp == null) { // if callerApp is null, the caller is the system process return false; @@ -93,8 +91,7 @@ class SenderFilter { private static final Filter SYSTEM_OR_SIGNATURE = new Filter() { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { return isSystemApp(callerApp, callerUid, callerPid) || ifw.signaturesMatch(callerUid, resolvedApp.uid); } @@ -103,8 +100,7 @@ class SenderFilter { private static final Filter USER_ID = new Filter() { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { // This checks whether the caller is either the system process, or has the same user id // I.e. the same app, or an app that uses the same shared user id. // This is the same set of applications that would be able to access the component if diff --git a/services/java/com/android/server/firewall/SenderPermissionFilter.java b/services/java/com/android/server/firewall/SenderPermissionFilter.java index 02d8b15..310da20 100644 --- a/services/java/com/android/server/firewall/SenderPermissionFilter.java +++ b/services/java/com/android/server/firewall/SenderPermissionFilter.java @@ -34,8 +34,7 @@ class SenderPermissionFilter implements Filter { @Override public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, - String callerPackage, int callerUid, int callerPid, String resolvedType, - ApplicationInfo resolvedApp) { + int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { // We assume the component is exported here. If the component is not exported, then // ActivityManager would only resolve to this component for callers from the same uid. // In this case, it doesn't matter whether the component is exported or not. diff --git a/services/java/com/android/server/firewall/StringFilter.java b/services/java/com/android/server/firewall/StringFilter.java index de5a69f..ed5d3f3 100644 --- a/services/java/com/android/server/firewall/StringFilter.java +++ b/services/java/com/android/server/firewall/StringFilter.java @@ -119,10 +119,9 @@ abstract class StringFilter implements Filter { protected abstract boolean matchesValue(String value); @Override - public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage, + public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) { - String value = mValueProvider.getValue(intent, callerApp, callerPackage, resolvedType, - resolvedApp); + String value = mValueProvider.getValue(intent, callerApp, resolvedType, resolvedApp); return matchesValue(value); } @@ -137,7 +136,7 @@ abstract class StringFilter implements Filter { } public abstract String getValue(Intent intent, ApplicationInfo callerApp, - String callerPackage, String resolvedType, ApplicationInfo resolvedApp); + String resolvedType, ApplicationInfo resolvedApp); } private static class EqualsFilter extends StringFilter { @@ -231,8 +230,8 @@ abstract class StringFilter implements Filter { public static final ValueProvider COMPONENT = new ValueProvider("component") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { ComponentName cn = intent.getComponent(); if (cn != null) { return cn.flattenToString(); @@ -243,8 +242,8 @@ abstract class StringFilter implements Filter { public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { ComponentName cn = intent.getComponent(); if (cn != null) { return cn.getClassName(); @@ -255,8 +254,8 @@ abstract class StringFilter implements Filter { public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { ComponentName cn = intent.getComponent(); if (cn != null) { return cn.getPackageName(); @@ -265,28 +264,18 @@ abstract class StringFilter implements Filter { } }; - public static final ValueProvider SENDER_PACKAGE = new ValueProvider("sender-package") { - @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { - // TODO: We can't trust this value, so maybe should check all packages in the caller process? - return callerPackage; - } - }; - - public static final FilterFactory ACTION = new ValueProvider("action") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { return intent.getAction(); } }; public static final ValueProvider DATA = new ValueProvider("data") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { Uri data = intent.getData(); if (data != null) { return data.toString(); @@ -297,16 +286,16 @@ abstract class StringFilter implements Filter { public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { return resolvedType; } }; public static final ValueProvider SCHEME = new ValueProvider("scheme") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { Uri data = intent.getData(); if (data != null) { return data.getScheme(); @@ -317,8 +306,8 @@ abstract class StringFilter implements Filter { public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { Uri data = intent.getData(); if (data != null) { return data.getSchemeSpecificPart(); @@ -329,8 +318,8 @@ abstract class StringFilter implements Filter { public static final ValueProvider HOST = new ValueProvider("host") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { Uri data = intent.getData(); if (data != null) { return data.getHost(); @@ -341,8 +330,8 @@ abstract class StringFilter implements Filter { public static final ValueProvider PATH = new ValueProvider("path") { @Override - public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage, - String resolvedType, ApplicationInfo resolvedApp) { + public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType, + ApplicationInfo resolvedApp) { Uri data = intent.getData(); if (data != null) { return data.getPath(); 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..1ebff67 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; @@ -314,6 +317,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,6 +372,10 @@ 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(); @@ -918,6 +927,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; @@ -1017,6 +1051,7 @@ public class GpsLocationProvider implements LocationProviderInterface { return ((mEngineCapabilities & capability) != 0); } + /** * called from native code to update our position. */ @@ -1320,6 +1355,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 +1752,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 ca7bba2..1b8ee82 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -2308,6 +2308,8 @@ public class PackageManagerService extends IPackageManager.Stub { } public void revokePermission(String packageName, String permissionName) { + int changedAppId = -1; + synchronized (mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { @@ -2335,6 +2337,30 @@ public class PackageManagerService extends IPackageManager.Stub { gp.gids = removeInts(gp.gids, bp.gids); } mSettings.writeLPr(); + changedAppId = ps.appId; + } + } + + if (changedAppId >= 0) { + // We changed the perm on someone, kill its processes. + IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + final int callingUserId = UserHandle.getCallingUserId(); + final long ident = Binder.clearCallingIdentity(); + try { + //XXX we should only revoke for the calling user's app permissions, + // but for now we impact all users. + //am.killUid(UserHandle.getUid(callingUserId, changedAppId), + // "revoke " + permissionName); + int[] users = sUserManager.getUserIds(); + for (int user : users) { + am.killUid(UserHandle.getUid(user, changedAppId), + "revoke " + permissionName); + } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } } } } @@ -8223,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; @@ -8647,16 +8691,10 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeLPr(); } } - // 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)); - } - } + if (outInfo != null) { + // A user ID was deleted here. Go through all users and remove it + // from KeyStore. + removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId); } } @@ -8816,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; } @@ -8968,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); @@ -8999,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( @@ -10655,19 +10723,18 @@ public class PackageManagerService extends IPackageManager.Stub { || mSettings.mReadExternalStorageEnforced != enforced) { mSettings.mReadExternalStorageEnforced = enforced; mSettings.writeLPr(); - - // kill any non-foreground processes so we restart them and - // grant/revoke the GID. - final IActivityManager am = ActivityManagerNative.getDefault(); - if (am != null) { - final long token = Binder.clearCallingIdentity(); - try { - am.killProcessesBelowForeground("setPermissionEnforcement"); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(token); - } - } + } + } + // kill any non-foreground processes so we restart them and + // grant/revoke the GID. + final IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + final long token = Binder.clearCallingIdentity(); + try { + am.killProcessesBelowForeground("setPermissionEnforcement"); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(token); } } } else { 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 d603cfa..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,12 +222,10 @@ 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 { - // create the temporary file - tmp = File.createTempFile("journal", "", dir); // create the parents for the destination file File parent = file.getParentFile(); parent.mkdirs(); @@ -235,6 +233,8 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { if (!parent.exists()) { throw new IOException("Failed to create directory " + parent.getCanonicalPath()); } + // create the temporary file + tmp = File.createTempFile("journal", "", dir); // mark tmp -rw-r--r-- tmp.setReadable(true, false); // write to it diff --git a/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java new file mode 100644 index 0000000..9185903 --- /dev/null +++ b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java @@ -0,0 +1,27 @@ +/* + * 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.updates; + +import com.android.server.firewall.IntentFirewall; + +public class IntentFirewallInstallReceiver extends ConfigUpdateInstallReceiver { + + public IntentFirewallInstallReceiver() { + super(IntentFirewall.getRulesFile().getParent(), IntentFirewall.getRulesFile().getName(), + "metadata/", "version"); + } +} 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 cbc42eb..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(); @@ -5365,6 +5379,9 @@ public class WindowManagerService extends IWindowManager.Stub if (maxLayer < winAnim.mSurfaceLayer) { maxLayer = winAnim.mSurfaceLayer; } + if (minLayer > winAnim.mSurfaceLayer) { + minLayer = winAnim.mSurfaceLayer; + } // Don't include wallpaper in bounds calculation if (!ws.mIsWallpaper) { @@ -5377,17 +5394,14 @@ public class WindowManagerService extends IWindowManager.Stub frame.union(left, top, right, bottom); } - if (ws.mAppToken != null && ws.mAppToken.token == appToken) { - if (minLayer > ws.mWinAnimator.mSurfaceLayer) { - minLayer = ws.mWinAnimator.mSurfaceLayer; - } - if (ws.isDisplayedLw()) { - screenshotReady = true; - } - if (fullscreen) { - // No point in continuing down through windows. - break; - } + if (ws.mAppToken != null && ws.mAppToken.token == appToken && + ws.isDisplayedLw()) { + screenshotReady = true; + } + + if (fullscreen) { + // No point in continuing down through windows. + break; } } @@ -5461,14 +5475,12 @@ public class WindowManagerService extends IWindowManager.Stub rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer); } } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES); - if (DEBUG_SCREENSHOT && retryCount > MAX_SCREENSHOT_RETRIES) { - Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken + " appWin=" - + (appWin == null ? "null" : (appWin + " drawState=" - + appWin.mWinAnimator.mDrawState))); - } + if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " + + retryCount + " of " + appToken + " appWin=" + (appWin == null ? + "null" : (appWin + " drawState=" + appWin.mWinAnimator.mDrawState))); if (rawss == null) { - Slog.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh + Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh + ") to layer " + maxLayer); return null; } @@ -5481,7 +5493,7 @@ public class WindowManagerService extends IWindowManager.Stub canvas.drawBitmap(rawss, matrix, null); canvas.setBitmap(null); - if (DEBUG_SCREENSHOT) { + if (true || DEBUG_SCREENSHOT) { // TEST IF IT's ALL BLACK int[] buffer = new int[bm.getWidth() * bm.getHeight()]; bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight()); @@ -5494,7 +5506,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (allBlack) { Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" + - (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null")); + (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") + + " minLayer=" + minLayer + " maxLayer=" + maxLayer); } } @@ -5743,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--) { @@ -6241,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; @@ -7037,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); } } @@ -7113,6 +7130,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { if (mClientFreezingScreen) { mClientFreezingScreen = false; + mLastFinishedFreezeSource = "client-timeout"; stopFreezingDisplayLocked(); } } @@ -8030,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; @@ -8418,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); } } } @@ -8931,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); } @@ -8941,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(); @@ -9227,6 +9251,7 @@ public class WindowManagerService extends IWindowManager.Stub mInnerFields.mOrientationChangeComplete = false; } else { mInnerFields.mOrientationChangeComplete = true; + mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource; if (mWindowsFreezingScreen) { doRequest = true; } @@ -9499,6 +9524,8 @@ public class WindowManagerService extends IWindowManager.Stub mScreenFrozenLock.acquire(); mDisplayFrozen = true; + mDisplayFreezeTime = SystemClock.elapsedRealtime(); + mLastFinishedFreezeSource = null; mInputMonitor.freezeInputDispatchingLw(); @@ -9553,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) { @@ -10077,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 { |