diff options
Diffstat (limited to 'services')
50 files changed, 2893 insertions, 993 deletions
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java index 77d5076..2fa23c9 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java @@ -125,8 +125,10 @@ public class AppWidgetService extends SystemService implements WidgetBackupProvi // Register for the boot completed broadcast, so we can send the // ENABLE broacasts. If we try to send them now, they time out, // because the system isn't ready to handle them yet. + IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + filter, null, null); // Register for configuration changes so we can update the names // of the widgets when the locale changes. @@ -135,7 +137,6 @@ public class AppWidgetService extends SystemService implements WidgetBackupProvi // Register for broadcasts about package install, etc., so we can // update the provider list. - IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 0d6f548..e2a8ca2 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -212,6 +212,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(Intent.ACTION_USER_SWITCHED); registerForAirplaneMode(filter); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 62deec2..d6ecb46 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -426,29 +426,33 @@ public class LocationManagerService extends ILocationManager.Stub { Slog.e(TAG, "no geocoder provider found"); } - // bind to fused provider - FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext); - FusedProxy fusedProxy = FusedProxy.createAndBind( - mContext, - mLocationHandler, - flpHardwareProvider.getLocationHardware(), - com.android.internal.R.bool.config_enableFusedLocationOverlay, - com.android.internal.R.string.config_fusedLocationProviderPackageName, - com.android.internal.R.array.config_locationProviderPackageNames); - if(fusedProxy == null) { - Slog.e(TAG, "No FusedProvider found."); - } - - // bind to geofence provider - GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, - com.android.internal.R.bool.config_enableGeofenceOverlay, - com.android.internal.R.string.config_geofenceProviderPackageName, - com.android.internal.R.array.config_locationProviderPackageNames, - mLocationHandler, - gpsProvider.getGpsGeofenceProxy(), - flpHardwareProvider.getGeofenceHardware()); - if (provider == null) { - Slog.e(TAG, "no geofence provider found"); + // bind to fused provider if supported + if (FlpHardwareProvider.isSupported()) { + FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext); + FusedProxy fusedProxy = FusedProxy.createAndBind( + mContext, + mLocationHandler, + flpHardwareProvider.getLocationHardware(), + com.android.internal.R.bool.config_enableFusedLocationOverlay, + com.android.internal.R.string.config_fusedLocationProviderPackageName, + com.android.internal.R.array.config_locationProviderPackageNames); + if(fusedProxy == null) { + Slog.e(TAG, "Unable to bind FusedProxy."); + } + + // bind to geofence provider + GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, + com.android.internal.R.bool.config_enableGeofenceOverlay, + com.android.internal.R.string.config_geofenceProviderPackageName, + com.android.internal.R.array.config_locationProviderPackageNames, + mLocationHandler, + gpsProvider.getGpsGeofenceProxy(), + flpHardwareProvider.getGeofenceHardware()); + if (provider == null) { + Slog.e(TAG, "Unable to bind FLP Geofence proxy."); + } + } else { + Slog.e(TAG, "FLP HAL not supported."); } String[] testProviderStrings = resources.getStringArray( diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 8a30e50..4f0c9b5 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -20,19 +20,24 @@ import android.Manifest.permission; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.net.INetworkScoreCache; import android.net.INetworkScoreService; -import android.net.NetworkKey; import android.net.NetworkScorerAppManager; -import android.net.RssiCurve; import android.net.ScoredNetwork; +import android.os.RemoteException; import android.text.TextUtils; +import android.util.Log; import com.android.internal.R; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; /** * Backing service for {@link android.net.NetworkScoreManager}. @@ -46,12 +51,11 @@ public class NetworkScoreService extends INetworkScoreService.Stub { private final Context mContext; - // TODO: Delete this temporary class once we have a real place for scores. - private final Map<NetworkKey, RssiCurve> mScoredNetworks; + private final Map<Integer, INetworkScoreCache> mScoreCaches; public NetworkScoreService(Context context) { mContext = context; - mScoredNetworks = new HashMap<>(); + mScoreCaches = new HashMap<>(); } /** Called when the system is ready to run third-party code but before it actually does so. */ @@ -76,10 +80,31 @@ public class NetworkScoreService extends INetworkScoreService.Stub { " is not the active scorer."); } - // TODO: Propagate these scores down to the network subsystem layer instead of just holding - // them in memory. + // Separate networks by type. + Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>(); for (ScoredNetwork network : networks) { - mScoredNetworks.put(network.networkKey, network.rssiCurve); + List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type); + if (networkList == null) { + networkList = new ArrayList<>(); + networksByType.put(network.networkKey.type, networkList); + } + networkList.add(network); + } + + // Pass the scores of each type down to the appropriate network scorer. + for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) { + INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey()); + if (scoreCache != null) { + try { + scoreCache.updateScores(entry.getValue()); + } catch (RemoteException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e); + } + } + } else if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding"); + } } return true; @@ -112,8 +137,29 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** Clear scores. Callers are responsible for checking permissions as appropriate. */ private void clearInternal() { - // TODO: Propagate the flush down to the network subsystem layer. - mScoredNetworks.clear(); + Set<INetworkScoreCache> cachesToClear = getScoreCaches(); + + for (INetworkScoreCache scoreCache : cachesToClear) { + try { + scoreCache.clearScores(); + } catch (RemoteException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Unable to clear scores", e); + } + } + } + } + + @Override + public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) { + mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG); + synchronized (mScoreCaches) { + if (mScoreCaches.containsKey(networkType)) { + throw new IllegalArgumentException( + "Score cache already registered for type " + networkType); + } + mScoreCaches.put(networkType, scoreCache); + } } @Override @@ -125,12 +171,28 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return; } writer.println("Current scorer: " + currentScorer); - if (mScoredNetworks.isEmpty()) { - writer.println("No networks scored."); - } else { - for (Map.Entry<NetworkKey, RssiCurve> entry : mScoredNetworks.entrySet()) { - writer.println(entry.getKey() + ": " + entry.getValue()); + + for (INetworkScoreCache scoreCache : getScoreCaches()) { + try { + scoreCache.asBinder().dump(fd, args); + } catch (RemoteException e) { + writer.println("Unable to dump score cache"); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Unable to dump score cache", e); + } } } } + + /** + * Returns a set of all score caches that are currently active. + * + * <p>May be used to perform an action on all score caches without potentially strange behavior + * if a new scorer is registered during that action's execution. + */ + private Set<INetworkScoreCache> getScoreCaches() { + synchronized (mScoreCaches) { + return new HashSet<>(mScoreCaches.values()); + } + } } diff --git a/services/core/java/com/android/server/RecognitionManagerService.java b/services/core/java/com/android/server/RecognitionManagerService.java index c2e749d..60d38ae 100644 --- a/services/core/java/com/android/server/RecognitionManagerService.java +++ b/services/core/java/com/android/server/RecognitionManagerService.java @@ -78,8 +78,10 @@ public class RecognitionManagerService extends Binder { mMonitor = new MyPackageMonitor(); mMonitor.register(context, null, UserHandle.ALL, true); mIPm = AppGlobals.getPackageManager(); + IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + filter, null, null); } public void systemReady() { diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java index 415fcc1..50cfe48 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -97,13 +97,15 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { mObserver = new WiredAccessoryObserver(); + IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context ctx, Intent intent) { bootCompleted(); } }, - new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + filter, null, null); } private void bootCompleted() { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0c91907..f908de2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -95,7 +95,6 @@ import android.app.INotificationManager; import android.app.IProcessObserver; import android.app.IServiceConnection; import android.app.IStopUserCallback; -import android.app.IThumbnailReceiver; import android.app.IUiAutomationConnection; import android.app.IUserSwitchObserver; import android.app.Instrumentation; @@ -703,13 +702,6 @@ public final class ActivityManagerService extends ActivityManagerNative String mBackupAppName = null; BackupRecord mBackupTarget = null; - /** - * List of PendingThumbnailsRecord objects of clients who are still - * waiting to receive all of the thumbnails for a task. - */ - final ArrayList<PendingThumbnailsRecord> mPendingThumbnails = - new ArrayList<PendingThumbnailsRecord>(); - final ProviderMap mProviderMap; /** @@ -1017,11 +1009,11 @@ public final class ActivityManagerService extends ActivityManagerNative static class ProcessChangeItem { static final int CHANGE_ACTIVITIES = 1<<0; - static final int CHANGE_IMPORTANCE= 1<<1; + static final int CHANGE_PROCESS_STATE = 1<<1; int changes; int uid; int pid; - int importance; + int processState; boolean foregroundActivities; } @@ -3200,11 +3192,10 @@ public final class ActivityManagerService extends ActivityManagerNative observer.onForegroundActivitiesChanged(item.pid, item.uid, item.foregroundActivities); } - if ((item.changes&ProcessChangeItem.CHANGE_IMPORTANCE) != 0) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "IMPORTANCE CHANGED pid=" - + item.pid + " uid=" + item.uid + ": " + item.importance); - observer.onImportanceChanged(item.pid, item.uid, - item.importance); + if ((item.changes&ProcessChangeItem.CHANGE_PROCESS_STATE) != 0) { + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "PROCSTATE CHANGED pid=" + + item.pid + " uid=" + item.uid + ": " + item.processState); + observer.onProcessStateChanged(item.pid, item.uid, item.processState); } } } catch (RemoteException e) { @@ -5453,21 +5444,15 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("File descriptors passed in Bundle"); } - ActivityRecord r = null; - final long origId = Binder.clearCallingIdentity(); synchronized (this) { - r = ActivityRecord.isInStackLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { r.task.stack.activityStoppedLocked(r, icicle, thumbnail, description); } } - if (r != null) { - sendPendingThumbnail(r, null, null, null, false); - } - trimApplications(); Binder.restoreCallingIdentity(origId); @@ -6983,66 +6968,24 @@ public final class ActivityManagerService extends ActivityManagerNative // ========================================================= @Override - public List<RunningTaskInfo> getTasks(int maxNum, int flags, - IThumbnailReceiver receiver) { + public List<RunningTaskInfo> getTasks(int maxNum, int flags) { + final int callingUid = Binder.getCallingUid(); ArrayList<RunningTaskInfo> list = new ArrayList<RunningTaskInfo>(); - PendingThumbnailsRecord pending = new PendingThumbnailsRecord(receiver); - ActivityRecord topRecord = null; - synchronized(this) { if (localLOGV) Slog.v( - TAG, "getTasks: max=" + maxNum + ", flags=" + flags - + ", receiver=" + receiver); + TAG, "getTasks: max=" + maxNum + ", flags=" + flags); - if (checkCallingPermission(android.Manifest.permission.GET_TASKS) - != PackageManager.PERMISSION_GRANTED) { - if (receiver != null) { - // If the caller wants to wait for pending thumbnails, - // it ain't gonna get them. - try { - receiver.finished(); - } catch (RemoteException ex) { - } - } - String msg = "Permission Denial: getTasks() from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " requires " + android.Manifest.permission.GET_TASKS; - Slog.w(TAG, msg); - throw new SecurityException(msg); + final boolean allowed = checkCallingPermission( + android.Manifest.permission.GET_TASKS) + == PackageManager.PERMISSION_GRANTED; + if (!allowed) { + Slog.w(TAG, "getTasks: caller " + callingUid + + " does not hold GET_TASKS; limiting output"); } // TODO: Improve with MRU list from all ActivityStacks. - topRecord = mStackSupervisor.getTasksLocked(maxNum, receiver, pending, list); - - if (!pending.pendingRecords.isEmpty()) { - mPendingThumbnails.add(pending); - } - } - - if (localLOGV) Slog.v(TAG, "We have pending thumbnails: " + pending); - - if (topRecord != null) { - if (localLOGV) Slog.v(TAG, "Requesting top thumbnail"); - try { - IApplicationThread topThumbnail = topRecord.app.thread; - topThumbnail.requestThumbnail(topRecord.appToken); - } catch (Exception e) { - Slog.w(TAG, "Exception thrown when requesting thumbnail", e); - sendPendingThumbnail(null, topRecord.appToken, null, null, true); - } - } - - if (pending.pendingRecords.isEmpty() && receiver != null) { - // In this case all thumbnails were available and the client - // is being asked to be told when the remaining ones come in... - // which is unusually, since the top-most currently running - // activity should never have a canned thumbnail! Oh well. - try { - receiver.finished(); - } catch (RemoteException ex) { - } + mStackSupervisor.getTasksLocked(maxNum, list, callingUid, allowed); } return list; @@ -7055,12 +6998,18 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) { - userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, + final int callingUid = Binder.getCallingUid(); + userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, false, true, "getRecentTasks", null); synchronized (this) { - enforceCallingPermission(android.Manifest.permission.GET_TASKS, - "getRecentTasks()"); + final boolean allowed = checkCallingPermission( + android.Manifest.permission.GET_TASKS) + == PackageManager.PERMISSION_GRANTED; + if (!allowed) { + Slog.w(TAG, "getRecentTasks: caller " + callingUid + + " does not hold GET_TASKS; limiting output"); + } final boolean detailed = checkCallingPermission( android.Manifest.permission.GET_DETAILED_TASKS) == PackageManager.PERMISSION_GRANTED; @@ -7095,6 +7044,13 @@ public final class ActivityManagerService extends ActivityManagerNative || (tr.intent == null) || ((tr.intent.getFlags() &Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0)) { + if (!allowed) { + // If the caller doesn't have the GET_TASKS permission, then only + // allow them to see a small subset of tasks -- their own and home. + if (!tr.isHomeTask() && tr.creatorUid != callingUid) { + continue; + } + } ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); rti.id = tr.numActivities > 0 ? tr.taskId : -1; @@ -7114,8 +7070,7 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList<ActivityRecord> activities = tr.mActivities; int activityNdx; final int numActivities = activities.size(); - for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; - ++activityNdx) { + for (activityNdx = 0; activityNdx < numActivities; ++activityNdx) { final ActivityRecord r = activities.get(activityNdx); if (r.intent != null && (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) @@ -7123,14 +7078,34 @@ public final class ActivityManagerService extends ActivityManagerNative break; } } - // Traverse downwards starting below break looking for set label and icon. - for (--activityNdx; activityNdx >= 0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - if (r.activityLabel != null || r.activityIcon != null) { - rti.activityLabel = r.activityLabel; - rti.activityIcon = r.activityIcon; - break; + if (activityNdx > 0) { + // Traverse downwards starting below break looking for set label, icon. + // Note that if there are activities in the task but none of them set the + // recent activity values, then we do not fall back to the last set + // values in the TaskRecord. + rti.activityValues = new ActivityManager.RecentsActivityValues(); + for (--activityNdx; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.activityValues != null) { + if (rti.activityValues.label == null) { + rti.activityValues.label = r.activityValues.label; + tr.lastActivityValues.label = r.activityValues.label; + } + if (rti.activityValues.icon == null) { + rti.activityValues.icon = r.activityValues.icon; + tr.lastActivityValues.icon = r.activityValues.icon; + } + if (rti.activityValues.colorPrimary == 0) { + rti.activityValues.colorPrimary = r.activityValues.colorPrimary; + tr.lastActivityValues.colorPrimary = r.activityValues.colorPrimary; + } + } } + } else { + // If there are no activity records in this task, then we use the last + // resolved values + rti.activityValues = + new ActivityManager.RecentsActivityValues(tr.lastActivityValues); } if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) { @@ -7198,13 +7173,11 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel, - Bitmap activityIcon) { + public void setRecentsActivityValues(IBinder token, ActivityManager.RecentsActivityValues rav) { synchronized (this) { ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { - r.activityLabel = activityLabel.toString(); - r.activityIcon = activityIcon; + r.activityValues = rav; } } } @@ -7636,82 +7609,6 @@ public final class ActivityManagerService extends ActivityManagerNative } // ========================================================= - // THUMBNAILS - // ========================================================= - - public void reportThumbnail(IBinder token, - Bitmap thumbnail, CharSequence description) { - //System.out.println("Report thumbnail for " + token + ": " + thumbnail); - final long origId = Binder.clearCallingIdentity(); - sendPendingThumbnail(null, token, thumbnail, description, true); - Binder.restoreCallingIdentity(origId); - } - - final void sendPendingThumbnail(ActivityRecord r, IBinder token, - Bitmap thumbnail, CharSequence description, boolean always) { - TaskRecord task; - ArrayList<PendingThumbnailsRecord> receivers = null; - - //System.out.println("Send pending thumbnail: " + r); - - synchronized(this) { - if (r == null) { - r = ActivityRecord.isInStackLocked(token); - if (r == null) { - return; - } - } - if (thumbnail == null && r.thumbHolder != null) { - thumbnail = r.thumbHolder.lastThumbnail; - description = r.thumbHolder.lastDescription; - } - if (thumbnail == null && !always) { - // If there is no thumbnail, and this entry is not actually - // going away, then abort for now and pick up the next - // thumbnail we get. - return; - } - task = r.task; - - int N = mPendingThumbnails.size(); - int i=0; - while (i<N) { - PendingThumbnailsRecord pr = mPendingThumbnails.get(i); - //System.out.println("Looking in " + pr.pendingRecords); - if (pr.pendingRecords.remove(r)) { - if (receivers == null) { - receivers = new ArrayList<PendingThumbnailsRecord>(); - } - receivers.add(pr); - if (pr.pendingRecords.size() == 0) { - pr.finished = true; - mPendingThumbnails.remove(i); - N--; - continue; - } - } - i++; - } - } - - if (receivers != null) { - final int N = receivers.size(); - for (int i=0; i<N; i++) { - try { - PendingThumbnailsRecord pr = receivers.get(i); - pr.receiver.newThumbnail( - task != null ? task.taskId : -1, thumbnail, description); - if (pr.finished) { - pr.receiver.finished(); - } - } catch (Exception e) { - Slog.w(TAG, "Exception thrown when sending thumbnail", e); - } - } - } - } - - // ========================================================= // CONTENT PROVIDERS // ========================================================= @@ -10635,6 +10532,7 @@ public final class ActivityManagerService extends ActivityManagerNative int adj = app.curAdj; outInfo.importance = oomAdjToImportance(adj, outInfo); outInfo.importanceReasonCode = app.adjTypeCode; + outInfo.processState = app.curProcState; } public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() { @@ -14690,7 +14588,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.keeping = true; app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT; - // System process can do UI, and when they do we want to have + // System processes can do UI, and when they do we want to have // them trim their memory after the user leaves the UI. To // facilitate this, here we need to determine whether or not it // is currently showing UI. @@ -15305,89 +15203,10 @@ public final class ActivityManagerService extends ActivityManagerNative // it when computing the final cached adj later. Note that we don't need to // worry about this for max adj above, since max adj will always be used to // keep it out of the cached vaues. - adj = app.modifyRawOomAdj(adj); - + app.curAdj = app.modifyRawOomAdj(adj); + app.curSchedGroup = schedGroup; app.curProcState = procState; - - int importance = app.memImportance; - if (importance == 0 || adj != app.curAdj || schedGroup != app.curSchedGroup) { - app.curAdj = adj; - app.curSchedGroup = schedGroup; - if (!interesting) { - // For this reporting, if there is not something explicitly - // interesting in this process then we will push it to the - // background importance. - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; - } else if (adj >= ProcessList.CACHED_APP_MIN_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; - } else if (adj >= ProcessList.SERVICE_B_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; - } else if (adj >= ProcessList.HOME_APP_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; - } else if (adj >= ProcessList.SERVICE_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; - } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE; - } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE; - } else if (adj >= ProcessList.VISIBLE_APP_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; - } else if (adj >= ProcessList.FOREGROUND_APP_ADJ) { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; - } else { - importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERSISTENT; - } - } - - int changes = importance != app.memImportance ? ProcessChangeItem.CHANGE_IMPORTANCE : 0; - if (foregroundActivities != app.foregroundActivities) { - changes |= ProcessChangeItem.CHANGE_ACTIVITIES; - } - if (changes != 0) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Changes in " + app + ": " + changes); - app.memImportance = importance; - app.foregroundActivities = foregroundActivities; - int i = mPendingProcessChanges.size()-1; - ProcessChangeItem item = null; - while (i >= 0) { - item = mPendingProcessChanges.get(i); - if (item.pid == app.pid) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Re-using existing item: " + item); - break; - } - i--; - } - if (i < 0) { - // No existing item in pending changes; need a new one. - final int NA = mAvailProcessChanges.size(); - if (NA > 0) { - item = mAvailProcessChanges.remove(NA-1); - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Retreiving available item: " + item); - } else { - item = new ProcessChangeItem(); - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Allocating new item: " + item); - } - item.changes = 0; - item.pid = app.pid; - item.uid = app.info.uid; - if (mPendingProcessChanges.size() == 0) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, - "*** Enqueueing dispatch processes changed!"); - mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget(); - } - mPendingProcessChanges.add(item); - } - item.changes |= changes; - item.importance = importance; - item.foregroundActivities = foregroundActivities; - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Item " - + Integer.toHexString(System.identityHashCode(item)) - + " " + app.toShortString() + ": changes=" + item.changes - + " importance=" + item.importance - + " foreground=" + item.foregroundActivities - + " type=" + app.adjType + " source=" + app.adjSource - + " target=" + app.adjTarget); - } + app.foregroundActivities = foregroundActivities; return app.curRawAdj; } @@ -15660,7 +15479,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping, - ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) { + ProcessRecord TOP_APP, boolean doingAll, long now) { boolean success = true; if (app.curRawAdj != app.setRawAdj) { @@ -15679,6 +15498,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.setRawAdj = app.curRawAdj; } + int changes = 0; + if (app.curAdj != app.setAdj) { ProcessList.setOomAdj(app.pid, app.curAdj); if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( @@ -15720,9 +15541,14 @@ public final class ActivityManagerService extends ActivityManagerNative app.curSchedGroup <= Process.THREAD_GROUP_BG_NONINTERACTIVE); } } + if (app.repForegroundActivities != app.foregroundActivities) { + app.repForegroundActivities = app.foregroundActivities; + changes |= ProcessChangeItem.CHANGE_ACTIVITIES; + } if (app.repProcState != app.curProcState) { app.repProcState = app.curProcState; - if (!reportingProcessState && app.thread != null) { + changes |= ProcessChangeItem.CHANGE_PROCESS_STATE; + if (app.thread != null) { try { if (false) { //RuntimeException h = new RuntimeException("here"); @@ -15767,6 +15593,51 @@ public final class ActivityManagerService extends ActivityManagerNative app.procStateChanged = true; } } + + if (changes != 0) { + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Changes in " + app + ": " + changes); + int i = mPendingProcessChanges.size()-1; + ProcessChangeItem item = null; + while (i >= 0) { + item = mPendingProcessChanges.get(i); + if (item.pid == app.pid) { + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Re-using existing item: " + item); + break; + } + i--; + } + if (i < 0) { + // No existing item in pending changes; need a new one. + final int NA = mAvailProcessChanges.size(); + if (NA > 0) { + item = mAvailProcessChanges.remove(NA-1); + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Retreiving available item: " + item); + } else { + item = new ProcessChangeItem(); + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Allocating new item: " + item); + } + item.changes = 0; + item.pid = app.pid; + item.uid = app.info.uid; + if (mPendingProcessChanges.size() == 0) { + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, + "*** Enqueueing dispatch processes changed!"); + mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget(); + } + mPendingProcessChanges.add(item); + } + item.changes |= changes; + item.processState = app.repProcState; + item.foregroundActivities = app.repForegroundActivities; + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Item " + + Integer.toHexString(System.identityHashCode(item)) + + " " + app.toShortString() + ": changes=" + item.changes + + " procState=" + item.processState + + " foreground=" + item.foregroundActivities + + " type=" + app.adjType + " source=" + app.adjSource + + " target=" + app.adjTarget); + } + return success; } @@ -15777,7 +15648,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj, - ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) { + ProcessRecord TOP_APP, boolean doingAll, long now) { if (app.thread == null) { return false; } @@ -15786,8 +15657,7 @@ public final class ActivityManagerService extends ActivityManagerNative computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now); - return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll, - reportingProcessState, now); + return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll, now); } final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground, @@ -15853,10 +15723,6 @@ public final class ActivityManagerService extends ActivityManagerNative } final boolean updateOomAdjLocked(ProcessRecord app) { - return updateOomAdjLocked(app, false); - } - - final boolean updateOomAdjLocked(ProcessRecord app, boolean doingProcessState) { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; final boolean wasCached = app.cached; @@ -15869,7 +15735,7 @@ public final class ActivityManagerService extends ActivityManagerNative // need to do a complete oom adj. final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ ? app.curRawAdj : ProcessList.UNKNOWN_ADJ; - boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, doingProcessState, + boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, SystemClock.uptimeMillis()); if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) { // Changed to/from cached state, so apps after it in the LRU @@ -16002,7 +15868,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - applyOomAdjLocked(app, wasKeeping, TOP_APP, true, false, now); + applyOomAdjLocked(app, wasKeeping, TOP_APP, true, now); // Count the number of process types. switch (app.curProcState) { diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 7a44473..f506eab 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -23,6 +23,7 @@ import com.android.server.AttributeCache; import com.android.server.am.ActivityStack.ActivityState; import com.android.server.am.ActivityStackSupervisor.ActivityContainer; +import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ResultInfo; import android.content.ComponentName; @@ -131,7 +132,6 @@ final class ActivityRecord { boolean sleeping; // have we told the activity to sleep? boolean waitingVisible; // true if waiting for a new act to become vis boolean nowVisible; // is this activity's window visible? - boolean thumbnailNeeded;// has someone requested a thumbnail? boolean idle; // has the activity gone idle? boolean hasBeenLaunched;// has this activity ever been launched? boolean frozenBeforeDestroy;// has been frozen but not yet destroyed. @@ -148,8 +148,7 @@ final class ActivityRecord { boolean mStartingWindowShown = false; ActivityContainer mInitialActivityContainer; - String activityLabel; - Bitmap activityIcon; + ActivityManager.RecentsActivityValues activityValues; // the recents information for this activity void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); @@ -239,7 +238,6 @@ final class ActivityRecord { pw.print(" immersive="); pw.print(immersive); pw.print(" launchMode="); pw.println(launchMode); pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy); - pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded); pw.print(" forceNewConfig="); pw.println(forceNewConfig); pw.print(prefix); pw.print("mActivityType="); pw.println(activityTypeToString(mActivityType)); @@ -375,7 +373,6 @@ final class ActivityRecord { visible = true; waitingVisible = false; nowVisible = false; - thumbnailNeeded = false; idle = false; hasBeenLaunched = false; mStackSupervisor = supervisor; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 6769c9c..d5ab277 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -53,7 +53,6 @@ import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityController; -import android.app.IThumbnailReceiver; import android.app.ResultInfo; import android.app.ActivityManager.RunningTaskInfo; import android.content.ComponentName; @@ -73,7 +72,6 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.util.EventLog; @@ -1098,6 +1096,36 @@ final class ActivityStack { ensureActivitiesVisibleLocked(r, starting, null, configChanges, forceHomeShown); } + // Checks if any of the stacks above this one has a fullscreen activity behind it. + // If so, this stack is hidden, otherwise it is visible. + private boolean isStackVisible() { + if (!isAttached()) { + return false; + } + + if (mStackSupervisor.isFrontStack(this)) { + return true; + } + + // Start at the task above this one and go up, looking for a visible + // fullscreen activity, or a translucent activity that requested the + // wallpaper to be shown behind it. + for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) { + final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks(); + for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) { + final ArrayList<ActivityRecord> activities = tasks.get(taskNdx).mActivities; + for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) { + final ActivityRecord r = activities.get(activityNdx); + if (!r.finishing && r.visible && r.fullscreen) { + return false; + } + } + } + } + + return true; + } + /** * Make sure that all activities that need to be visible (that is, they * currently can be seen by the user) actually are. @@ -1122,8 +1150,8 @@ final class ActivityStack { // make sure any activities under it are now visible. boolean aboveTop = true; boolean showHomeBehindStack = false; - boolean behindFullscreen = !mStackSupervisor.isFrontStack(this) && - !(forceHomeShown && isHomeStack()); + boolean behindFullscreen = !isStackVisible(); + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); final ArrayList<ActivityRecord> activities = task.mActivities; @@ -2537,13 +2565,6 @@ final class ActivityStack { finishActivityResultsLocked(r, resultCode, resultData); - if (!mService.mPendingThumbnails.isEmpty()) { - // There are clients waiting to receive thumbnails so, in case - // this is an activity that someone is waiting for, add it - // to the pending list so we can correctly update the clients. - mStackSupervisor.mCancelledThumbnails.add(r); - } - if (mResumedActivity == r) { boolean endTask = index <= 0; if (DEBUG_VISBILITY || DEBUG_TRANSITION) Slog.v(TAG, @@ -2782,13 +2803,6 @@ final class ActivityStack { cleanUpActivityServicesLocked(r); } - if (!mService.mPendingThumbnails.isEmpty()) { - // There are clients waiting to receive thumbnails so, in case - // this is an activity that someone is waiting for, add it - // to the pending list so we can correctly update the clients. - mStackSupervisor.mCancelledThumbnails.add(r); - } - // Get rid of any pending idle timeouts. removeTimeoutsForActivityLocked(r); } @@ -3535,9 +3549,7 @@ final class ActivityStack { return didSomething; } - ActivityRecord getTasksLocked(IThumbnailReceiver receiver, - PendingThumbnailsRecord pending, List<RunningTaskInfo> list) { - ActivityRecord topRecord = null; + void getTasksLocked(List<RunningTaskInfo> list, int callingUid, boolean allowed) { for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); ActivityRecord r = null; @@ -3548,6 +3560,9 @@ final class ActivityStack { if (activities.isEmpty()) { continue; } + if (!allowed && !task.isHomeTask() && task.creatorUid != callingUid) { + continue; + } for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { r = activities.get(activityNdx); @@ -3581,23 +3596,8 @@ final class ActivityStack { ci.numRunning = numRunning; //System.out.println( // "#" + maxNum + ": " + " descr=" + ci.description); - if (receiver != null) { - if (localLOGV) Slog.v( - TAG, "State=" + top.state + "Idle=" + top.idle - + " app=" + top.app - + " thr=" + (top.app != null ? top.app.thread : null)); - if (top.state == ActivityState.RESUMED || top.state == ActivityState.PAUSING) { - if (top.idle && top.app != null && top.app.thread != null) { - topRecord = top; - } else { - top.thumbnailNeeded = true; - } - } - pending.pendingRecords.add(top); - } list.add(ci); } - return topRecord; } public void unhandledBackLocked() { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 3770a07..9107cb6 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -41,7 +41,6 @@ import android.app.IActivityContainer; import android.app.IActivityContainerCallback; import android.app.IActivityManager; import android.app.IApplicationThread; -import android.app.IThumbnailReceiver; import android.app.PendingIntent; import android.app.ActivityManager.RunningTaskInfo; import android.app.IActivityManager.WaitResult; @@ -191,10 +190,6 @@ public final class ActivityStackSupervisor implements DisplayListener { /** List of activities that are in the process of going to sleep. */ final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<ActivityRecord>(); - /** List of ActivityRecord objects that have been finished and must still report back to a - * pending thumbnail receiver. */ - final ArrayList<ActivityRecord> mCancelledThumbnails = new ArrayList<ActivityRecord>(); - /** Used on user changes */ final ArrayList<UserStartedState> mStartingUsers = new ArrayList<UserStartedState>(); @@ -591,10 +586,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return null; } - ActivityRecord getTasksLocked(int maxNum, IThumbnailReceiver receiver, - PendingThumbnailsRecord pending, List<RunningTaskInfo> list) { - ActivityRecord r = null; - + void getTasksLocked(int maxNum, List<RunningTaskInfo> list, int callingUid, boolean allowed) { // Gather all of the running tasks for each stack into runningTaskLists. ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = new ArrayList<ArrayList<RunningTaskInfo>>(); @@ -605,10 +597,7 @@ public final class ActivityStackSupervisor implements DisplayListener { final ActivityStack stack = stacks.get(stackNdx); ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<RunningTaskInfo>(); runningTaskLists.add(stackTaskList); - final ActivityRecord ar = stack.getTasksLocked(receiver, pending, stackTaskList); - if (r == null && isFrontStack(stack)) { - r = ar; - } + stack.getTasksLocked(stackTaskList, callingUid, allowed); } } @@ -635,8 +624,6 @@ public final class ActivityStackSupervisor implements DisplayListener { break; } } - - return r; } ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags, @@ -1912,7 +1899,6 @@ public final class ActivityStackSupervisor implements DisplayListener { ArrayList<UserStartedState> startingUsers = null; int NS = 0; int NF = 0; - IApplicationThread sendThumbnail = null; boolean booting = false; boolean enableScreen = false; boolean activityRemoved = false; @@ -1940,11 +1926,6 @@ public final class ActivityStackSupervisor implements DisplayListener { // us, we can now deliver. r.idle = true; - if (r.thumbnailNeeded && r.app != null && r.app.thread != null) { - sendThumbnail = r.app.thread; - r.thumbnailNeeded = false; - } - //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout); if (!mService.mBooted && isFrontStack(r.task.stack)) { mService.mBooted = true; @@ -1976,15 +1957,6 @@ public final class ActivityStackSupervisor implements DisplayListener { mFinishingActivities.clear(); } - final ArrayList<ActivityRecord> thumbnails; - final int NT = mCancelledThumbnails.size(); - if (NT > 0) { - thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails); - mCancelledThumbnails.clear(); - } else { - thumbnails = null; - } - if (isFrontStack(mHomeStack)) { booting = mService.mBooting; mService.mBooting = false; @@ -1995,28 +1967,6 @@ public final class ActivityStackSupervisor implements DisplayListener { mStartingUsers.clear(); } - // Perform the following actions from unsynchronized state. - final IApplicationThread thumbnailThread = sendThumbnail; - mHandler.post(new Runnable() { - @Override - public void run() { - if (thumbnailThread != null) { - try { - thumbnailThread.requestThumbnail(token); - } catch (Exception e) { - Slog.w(TAG, "Exception thrown when requesting thumbnail", e); - mService.sendPendingThumbnail(null, token, null, null, true); - } - } - - // Report back to any thumbnail receivers. - for (int i = 0; i < NT; i++) { - ActivityRecord r = thumbnails.get(i); - mService.sendPendingThumbnail(r, null, null, null, true); - } - } - }); - // Stop any activities that are scheduled to do so but have been // waiting for the next one to start. for (int i = 0; i < NS; i++) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index dbe773c..83e8a4b 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -110,6 +110,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub { return data; } + public long computeBatteryTimeRemaining() { + synchronized (mStats) { + long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime()); + return time >= 0 ? (time/1000) : time; + } + } + + public long computeChargeTimeRemaining() { + synchronized (mStats) { + long time = mStats.computeChargeTimeRemaining(SystemClock.elapsedRealtime()); + return time >= 0 ? (time/1000) : time; + } + } + public void addIsolatedUid(int isolatedUid, int appUid) { enforceCallingPermission(); synchronized (mStats) { diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index b15fa5d..9d6481a 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -503,7 +503,7 @@ public final class BroadcastQueue { // are already core system stuff so don't matter for this. r.curApp = filter.receiverList.app; filter.receiverList.app.curReceiver = r; - mService.updateOomAdjLocked(r.curApp, true); + mService.updateOomAdjLocked(r.curApp); } } try { diff --git a/services/core/java/com/android/server/am/PendingThumbnailsRecord.java b/services/core/java/com/android/server/am/PendingThumbnailsRecord.java deleted file mode 100644 index e4eb4d0..0000000 --- a/services/core/java/com/android/server/am/PendingThumbnailsRecord.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.am; - -import android.app.IThumbnailReceiver; - -import java.util.HashSet; - -/** - * This class keeps track of calls to getTasks() that are still - * waiting for thumbnail images. - */ -final class PendingThumbnailsRecord -{ - final IThumbnailReceiver receiver; // who is waiting. - final HashSet<ActivityRecord> pendingRecords; // HistoryRecord objects we still wait for. - boolean finished; // Is pendingRecords empty? - - PendingThumbnailsRecord(IThumbnailReceiver _receiver) - { - receiver = _receiver; - pendingRecords = new HashSet<ActivityRecord>(); - finished = false; - } -} diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index d04a6b2..8d7d300 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -77,7 +77,6 @@ final class ProcessRecord { int curSchedGroup; // Currently desired scheduling class int setSchedGroup; // Last set to background scheduling class int trimMemoryLevel; // Last selected memory trimming level - int memImportance; // Importance constant computed from curAdj int curProcState = -1; // Currently computed process state: ActivityManager.PROCESS_STATE_* int repProcState = -1; // Last reported process state int setProcState = -1; // Last set process state in process tracker @@ -91,6 +90,7 @@ final class ProcessRecord { boolean hasStartedServices; // Are there any started services running in this process? boolean foregroundServices; // Running any services that are foreground? boolean foregroundActivities; // Running any activities that are foreground? + boolean repForegroundActivities; // Last reported foreground activities. boolean systemNoUi; // This is a system process, but not currently showing UI. boolean hasShownUi; // Has UI been shown in this process since it was started? boolean pendingUiClean; // Want to clean up resources from showing UI? @@ -267,9 +267,10 @@ final class ProcessRecord { pw.print(prefix); pw.print("persistent="); pw.print(persistent); pw.print(" removed="); pw.println(removed); } - if (hasClientActivities || foregroundActivities) { + if (hasClientActivities || foregroundActivities || repForegroundActivities) { pw.print(prefix); pw.print("hasClientActivities="); pw.print(hasClientActivities); - pw.print(" foregroundActivities="); pw.println(foregroundActivities); + pw.print(" foregroundActivities="); pw.print(foregroundActivities); + pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")"); } if (hasStartedServices) { pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 68da54d..9f0bc10 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -52,9 +52,15 @@ final class TaskRecord extends ThumbnailHolder { String stringName; // caching of toString() result. int userId; // user for which this task was created + int creatorUid; // The app uid that originally created the task int numFullscreen; // Number of fullscreen activities. + // This represents the last resolved activity values for this task + // NOTE: This value needs to be persisted with each task + ActivityManager.RecentsActivityValues lastActivityValues = + new ActivityManager.RecentsActivityValues(); + /** List of all activities in the task arranged in history order */ final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>(); @@ -131,9 +137,8 @@ final class TaskRecord extends ThumbnailHolder { rootWasReset = true; } - if (info.applicationInfo != null) { - userId = UserHandle.getUserId(info.applicationInfo.uid); - } + userId = UserHandle.getUserId(info.applicationInfo.uid); + creatorUid = info.applicationInfo.uid; } void disposeThumbnail() { diff --git a/services/core/java/com/android/server/am/UsageStatsService.java b/services/core/java/com/android/server/am/UsageStatsService.java index 587f949..42cf900 100644 --- a/services/core/java/com/android/server/am/UsageStatsService.java +++ b/services/core/java/com/android/server/am/UsageStatsService.java @@ -73,42 +73,44 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final boolean localLOGV = false; private static final boolean REPORT_UNEXPECTED = false; private static final String TAG = "UsageStats"; - + // Current on-disk Parcel version private static final int VERSION = 1008; private static final int CHECKIN_VERSION = 4; - + private static final String FILE_PREFIX = "usage-"; private static final String FILE_HISTORY = FILE_PREFIX + "history.xml"; - private static final int FILE_WRITE_INTERVAL = 30*60*1000; //ms - + private static final int FILE_WRITE_INTERVAL = (localLOGV) ? 0 : 30*60*1000; // 30m in ms + private static final int MAX_NUM_FILES = 5; - + private static final int NUM_LAUNCH_TIME_BINS = 10; private static final int[] LAUNCH_TIME_BINS = { 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000 }; - + static IUsageStats sService; private Context mContext; // structure used to maintain statistics since the last checkin. - final private ArrayMap<String, PkgUsageStatsExtended> mStats; + final private ArrayMap<String, PkgUsageStatsExtended> mStats + = new ArrayMap<String, PkgUsageStatsExtended>(); // Maintains the last time any component was resumed, for all time. - final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes; + final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes + = new ArrayMap<String, ArrayMap<String, Long>>(); // To remove last-resume time stats when a pacakge is removed. private PackageMonitor mPackageMonitor; // Lock to update package stats. Methods suffixed by SLOCK should invoked with // this lock held - final Object mStatsLock; + final Object mStatsLock = new Object(); // Lock to write to file. Methods suffixed by FLOCK should invoked with // this lock held. - final Object mFileLock; + final Object mFileLock = new Object(); // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks private String mLastResumedPkg; private String mLastResumedComp; @@ -118,52 +120,53 @@ public final class UsageStatsService extends IUsageStats.Stub { private String mFileLeaf; private File mDir; - private Calendar mCal; // guarded by itself + private final Calendar mCal // guarded by itself + = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); private final AtomicInteger mLastWriteDay = new AtomicInteger(-1); private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0); private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false); - + static class TimeStats { - int count; - int[] times = new int[NUM_LAUNCH_TIME_BINS]; - + int mCount; + final int[] mTimes = new int[NUM_LAUNCH_TIME_BINS]; + TimeStats() { } - + void incCount() { - count++; + mCount++; } - + void add(int val) { final int[] bins = LAUNCH_TIME_BINS; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { if (val < bins[i]) { - times[i]++; + mTimes[i]++; return; } } - times[NUM_LAUNCH_TIME_BINS-1]++; + mTimes[NUM_LAUNCH_TIME_BINS-1]++; } - + TimeStats(Parcel in) { - count = in.readInt(); - final int[] localTimes = times; + mCount = in.readInt(); + final int[] localTimes = mTimes; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { localTimes[i] = in.readInt(); } } - + void writeToParcel(Parcel out) { - out.writeInt(count); - final int[] localTimes = times; + out.writeInt(mCount); + final int[] localTimes = mTimes; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { out.writeInt(localTimes[i]); } } } - - private class PkgUsageStatsExtended { + + static class PkgUsageStatsExtended { final ArrayMap<String, TimeStats> mLaunchTimes = new ArrayMap<String, TimeStats>(); final ArrayMap<String, TimeStats> mFullyDrawnTimes @@ -172,18 +175,18 @@ public final class UsageStatsService extends IUsageStats.Stub { long mUsageTime; long mPausedTime; long mResumedTime; - + PkgUsageStatsExtended() { mLaunchCount = 0; mUsageTime = 0; } - + PkgUsageStatsExtended(Parcel in) { mLaunchCount = in.readInt(); mUsageTime = in.readLong(); if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount + ", Usage time:" + mUsageTime); - + final int numLaunchTimeStats = in.readInt(); if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats); mLaunchTimes.ensureCapacity(numLaunchTimeStats); @@ -207,16 +210,16 @@ public final class UsageStatsService extends IUsageStats.Stub { void updateResume(String comp, boolean launched) { if (launched) { - mLaunchCount ++; + mLaunchCount++; } mResumedTime = SystemClock.elapsedRealtime(); } - + void updatePause() { mPausedTime = SystemClock.elapsedRealtime(); mUsageTime += (mPausedTime - mResumedTime); } - + void addLaunchCount(String comp) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -225,7 +228,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } times.incCount(); } - + void addLaunchTime(String comp, int millis) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -260,7 +263,7 @@ public final class UsageStatsService extends IUsageStats.Stub { mFullyDrawnTimes.valueAt(i).writeToParcel(out); } } - + void clear() { mLaunchTimes.clear(); mFullyDrawnTimes.clear(); @@ -268,32 +271,25 @@ public final class UsageStatsService extends IUsageStats.Stub { mUsageTime = 0; } } - + UsageStatsService(String dir) { - mStats = new ArrayMap<String, PkgUsageStatsExtended>(); - mLastResumeTimes = new ArrayMap<String, ArrayMap<String, Long>>(); - mStatsLock = new Object(); - mFileLock = new Object(); + if (localLOGV) Slog.v(TAG, "UsageStatsService: " + dir); mDir = new File(dir); - mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); - mDir.mkdir(); - - // Remove any old usage files from previous versions. + + // Remove any old /data/system/usagestats.* files from previous versions. File parentDir = mDir.getParentFile(); - String fList[] = parentDir.list(); - if (fList != null) { + String files[] = parentDir.list(); + if (files != null) { String prefix = mDir.getName() + "."; - int i = fList.length; - while (i > 0) { - i--; - if (fList[i].startsWith(prefix)) { - Slog.i(TAG, "Deleting old usage file: " + fList[i]); - (new File(parentDir, fList[i])).delete(); + for (String file : files) { + if (file.startsWith(prefix)) { + Slog.i(TAG, "Deleting old usage file: " + file); + (new File(parentDir, file)).delete(); } } } - + // Update current stats which are binned by date mFileLeaf = getCurrentDateStr(FILE_PREFIX); mFile = new File(mDir, mFileLeaf); @@ -310,11 +306,11 @@ public final class UsageStatsService extends IUsageStats.Stub { */ private String getCurrentDateStr(String prefix) { StringBuilder sb = new StringBuilder(); + if (prefix != null) { + sb.append(prefix); + } synchronized (mCal) { mCal.setTimeInMillis(System.currentTimeMillis()); - if (prefix != null) { - sb.append(prefix); - } sb.append(mCal.get(Calendar.YEAR)); int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1; if (mm < 10) { @@ -329,17 +325,20 @@ public final class UsageStatsService extends IUsageStats.Stub { } return sb.toString(); } - + private Parcel getParcelForFile(File file) throws IOException { FileInputStream stream = new FileInputStream(file); - byte[] raw = readFully(stream); - Parcel in = Parcel.obtain(); - in.unmarshall(raw, 0, raw.length); - in.setDataPosition(0); - stream.close(); - return in; + try { + byte[] raw = readFully(stream); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + return in; + } finally { + stream.close(); + } } - + private void readStatsFromFile() { File newFile = mFile; synchronized (mFileLock) { @@ -356,12 +355,13 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + private void readStatsFLOCK(File file) throws IOException { Parcel in = getParcelForFile(file); int vers = in.readInt(); - if (vers != VERSION) { - Slog.w(TAG, "Usage stats version changed; dropping"); + if (vers != VERSION) { // vers will be 0 if the parcel file was empty + Slog.w(TAG, "Usage stats version of " + file + " changed from " + vers + " to " + + VERSION + "; dropping"); return; } int N = in.readInt(); @@ -382,12 +382,12 @@ public final class UsageStatsService extends IUsageStats.Stub { private void readHistoryStatsFromFile() { synchronized (mFileLock) { if (mHistoryFile.getBaseFile().exists()) { - readHistoryStatsFLOCK(mHistoryFile); + readHistoryStatsFLOCK(); } } } - private void readHistoryStatsFLOCK(AtomicFile file) { + private void readHistoryStatsFLOCK() { FileInputStream fis = null; try { fis = mHistoryFile.openRead(); @@ -470,12 +470,12 @@ public final class UsageStatsService extends IUsageStats.Stub { } return fileList; } - + private void checkFileLimitFLOCK() { // Get all usage stats output files ArrayList<String> fileList = getUsageStatsFileListFLOCK(); if (fileList == null) { - // Strange but we dont have to delete any thing + // Empty /data/system/usagestats/ so we don't have anything to delete return; } int count = fileList.size(); @@ -575,8 +575,8 @@ public final class UsageStatsService extends IUsageStats.Stub { } if (dayChanged || forceWriteHistoryStats) { - // Write history stats daily, or when forced (due to shutdown). - writeHistoryStatsFLOCK(mHistoryFile); + // Write history stats daily or when forced (due to shutdown) or when debugging. + writeHistoryStatsFLOCK(); } // Delete the backup file @@ -638,10 +638,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - private void writeHistoryStatsFLOCK(AtomicFile historyFile) { + private void writeHistoryStatsFLOCK() { FileOutputStream fos = null; try { - fos = historyFile.startWrite(); + fos = mHistoryFile.startWrite(); XmlSerializer out = new FastXmlSerializer(); out.setOutput(fos, "utf-8"); out.startDocument(null, true); @@ -664,11 +664,11 @@ public final class UsageStatsService extends IUsageStats.Stub { out.endTag(null, "usage-history"); out.endDocument(); - historyFile.finishWrite(fos); + mHistoryFile.finishWrite(fos); } catch (IOException e) { Slog.w(TAG,"Error writing history stats" + e); if (fos != null) { - historyFile.failWrite(fos); + mHistoryFile.failWrite(fos); } } } @@ -711,7 +711,8 @@ public final class UsageStatsService extends IUsageStats.Stub { sService = asInterface(b); return sService; } - + + @Override public void noteResumeComponent(ComponentName componentName) { enforceCallingPermission(); String pkgName; @@ -720,7 +721,7 @@ public final class UsageStatsService extends IUsageStats.Stub { ((pkgName = componentName.getPackageName()) == null)) { return; } - + final boolean samePackage = pkgName.equals(mLastResumedPkg); if (mIsResumed) { if (mLastResumedPkg != null) { @@ -734,14 +735,14 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + final boolean sameComp = samePackage && componentName.getClassName().equals(mLastResumedComp); - + mIsResumed = true; mLastResumedPkg = pkgName; mLastResumedComp = componentName.getClassName(); - + if (localLOGV) Slog.i(TAG, "started component:" + pkgName); PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { @@ -762,9 +763,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } } + @Override public void notePauseComponent(ComponentName componentName) { enforceCallingPermission(); - + synchronized (mStatsLock) { String pkgName; if ((componentName == null) || @@ -777,9 +779,9 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } mIsResumed = false; - + if (localLOGV) Slog.i(TAG, "paused component:"+pkgName); - + PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { // Weird some error here @@ -788,11 +790,12 @@ public final class UsageStatsService extends IUsageStats.Stub { } pus.updatePause(); } - + // Persist current data to file if needed. writeStatsToFile(false, false); } - + + @Override public void noteLaunchTime(ComponentName componentName, int millis) { enforceCallingPermission(); String pkgName; @@ -800,10 +803,10 @@ public final class UsageStatsService extends IUsageStats.Stub { ((pkgName = componentName.getPackageName()) == null)) { return; } - + // Persist current data to file if needed. writeStatsToFile(false, false); - + synchronized (mStatsLock) { PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus != null) { @@ -811,7 +814,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + public void noteFullyDrawnTime(ComponentName componentName, int millis) { enforceCallingPermission(); String pkgName; @@ -838,7 +841,8 @@ public final class UsageStatsService extends IUsageStats.Stub { mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null); } - + + @Override public PkgUsageStats getPkgUsageStats(ComponentName componentName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); @@ -858,7 +862,8 @@ public final class UsageStatsService extends IUsageStats.Stub { return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes); } } - + + @Override public PkgUsageStats[] getAllPkgUsageStats() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); @@ -884,8 +889,8 @@ public final class UsageStatsService extends IUsageStats.Stub { return retArr; } } - - static byte[] readFully(FileInputStream stream) throws java.io.IOException { + + static byte[] readFully(FileInputStream stream) throws IOException { int pos = 0; int avail = stream.available(); byte[] data = new byte[avail]; @@ -903,7 +908,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } } } - + private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput, boolean deleteAfterPrint, HashSet<String> packages) { List<String> fileList = getUsageStatsFileListFLOCK(); @@ -932,15 +937,12 @@ public final class UsageStatsService extends IUsageStats.Stub { // Delete old file after collecting info only for checkin requests dFile.delete(); } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file); - return; } catch (IOException e) { Slog.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file); - } + } } } - + private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw, String date, boolean isCompactOutput, HashSet<String> packages) { StringBuilder sb = new StringBuilder(512); @@ -951,19 +953,19 @@ public final class UsageStatsService extends IUsageStats.Stub { } else { sb.append("Date: "); } - + sb.append(date); - + int vers = in.readInt(); if (vers != VERSION) { sb.append(" (old data version)"); pw.println(sb.toString()); return; } - + pw.println(sb.toString()); int N = in.readInt(); - + while (N > 0) { N--; String pkgName = in.readString(); @@ -990,10 +992,10 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(activity); TimeStats times = pus.mLaunchTimes.valueAt(i); sb.append(','); - sb.append(times.count); + sb.append(times.mCount); for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) { sb.append(","); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } sb.append('\n'); } @@ -1005,7 +1007,7 @@ public final class UsageStatsService extends IUsageStats.Stub { TimeStats times = pus.mFullyDrawnTimes.valueAt(i); for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) { sb.append(","); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } sb.append('\n'); } @@ -1025,26 +1027,26 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(pus.mLaunchTimes.keyAt(i)); TimeStats times = pus.mLaunchTimes.valueAt(i); sb.append(": "); - sb.append(times.count); + sb.append(times.mCount); sb.append(" starts"); int lastBin = 0; for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) { - if (times.times[j] != 0) { + if (times.mTimes[j] != 0) { sb.append(", "); sb.append(lastBin); sb.append('-'); sb.append(LAUNCH_TIME_BINS[j]); sb.append("ms="); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } lastBin = LAUNCH_TIME_BINS[j]; } - if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { + if (times.mTimes[NUM_LAUNCH_TIME_BINS-1] != 0) { sb.append(", "); sb.append(">="); sb.append(lastBin); sb.append("ms="); - sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); + sb.append(times.mTimes[NUM_LAUNCH_TIME_BINS-1]); } sb.append('\n'); } @@ -1057,7 +1059,7 @@ public final class UsageStatsService extends IUsageStats.Stub { boolean needComma = false; int lastBin = 0; for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) { - if (times.times[j] != 0) { + if (times.mTimes[j] != 0) { if (needComma) { sb.append(", "); } else { @@ -1067,27 +1069,27 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append('-'); sb.append(LAUNCH_TIME_BINS[j]); sb.append("ms="); - sb.append(times.times[j]); + sb.append(times.mTimes[j]); } lastBin = LAUNCH_TIME_BINS[j]; } - if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { + if (times.mTimes[NUM_LAUNCH_TIME_BINS-1] != 0) { if (needComma) { sb.append(", "); } sb.append(">="); sb.append(lastBin); sb.append("ms="); - sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); + sb.append(times.mTimes[NUM_LAUNCH_TIME_BINS-1]); } sb.append('\n'); } } - + pw.write(sb.toString()); } } - + /** * Searches array of arguments for the specified string * @param args array of argument strings @@ -1104,7 +1106,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } return false; } - + /** * Searches array of arguments for the specified string's data * @param args array of argument strings @@ -1123,11 +1125,11 @@ public final class UsageStatsService extends IUsageStats.Stub { } return null; } - - @Override + /* - * The data persisted to file is parsed and the stats are computed. + * The data persisted to file is parsed and the stats are computed. */ + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -1141,23 +1143,23 @@ public final class UsageStatsService extends IUsageStats.Stub { final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c"); final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d"); final String rawPackages = scanArgsData(args, "--packages"); - + // Make sure the current stats are written to the file. This // doesn't need to be done if we are deleting files after printing, - // since it that case we won't print the current stats. + // since in that case we won't print the current stats. if (!deleteAfterPrint) { writeStatsToFile(true, false); } - + HashSet<String> packages = null; if (rawPackages != null) { if (!"*".equals(rawPackages)) { // A * is a wildcard to show all packages. String[] names = rawPackages.split(","); + if (names.length != 0) { + packages = new HashSet<String>(); + } for (String n : names) { - if (packages == null) { - packages = new HashSet<String>(); - } packages.add(n); } } @@ -1167,7 +1169,7 @@ public final class UsageStatsService extends IUsageStats.Stub { Slog.w(TAG, "Checkin without packages"); return; } - + synchronized (mFileLock) { collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages); } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 026fd29..1b40cdf 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -411,6 +411,7 @@ public class SyncManager { if (!factoryTest) { intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); context.registerReceiver(mBootCompletedReceiver, intentFilter); } diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java new file mode 100644 index 0000000..296cc5b --- /dev/null +++ b/services/core/java/com/android/server/hdmi/FeatureAction.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2014 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.hdmi; + +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Encapsulates a sequence of CEC/MHL command exchange for a certain feature. + * + * <p>Many CEC/MHL features are accomplished by CEC devices on the bus exchanging + * more than one command. {@link FeatureAction} represents the life cycle of the communication, + * manages the state as the process progresses, and if necessary, returns the result + * to the caller which initiates the action, through the callback given at the creation + * of the object. All the actual action classes inherit FeatureAction. + * + * <p>More than one FeatureAction objects can be up and running simultaneously, + * maintained by {@link HdmiControlService}. Each action is passed a new command + * arriving from the bus, and either consumes it if the command is what the action expects, + * or yields it to other action. + * + * Declared as package private, accessed by {@link HdmiControlService} only. + */ +abstract class FeatureAction { + + private static final String TAG = "FeatureAction"; + + // Timer handler message used for timeout event + protected static final int MSG_TIMEOUT = 100; + + // Default timeout for the incoming command to arrive in response to a request + protected static final int TIMEOUT_MS = 1000; + + // Default state used in common by all the feature actions. + protected static final int STATE_NONE = 0; + + // Internal state indicating the progress of action. + protected int mState = STATE_NONE; + + protected final HdmiControlService mService; + + // Logical address of the device for which the feature action is taken. The commands + // generated in an action all use this field as source address. + protected final int mSourceAddress; + + // Timer that manages timeout events. + protected ActionTimer mActionTimer; + + FeatureAction(HdmiControlService service, int sourceAddress) { + mService = service; + mSourceAddress = sourceAddress; + mActionTimer = createActionTimer(service.getServiceLooper()); + } + + @VisibleForTesting + void setActionTimer(ActionTimer actionTimer) { + mActionTimer = actionTimer; + } + + /** + * Called right after the action is created. Initialization or first step to take + * for the action can be done in this method. + * + * @return true if the operation is successful; otherwise false. + */ + abstract boolean start(); + + /** + * Process the command. Called whenever a new command arrives. + * + * @param cmd command to process + * @return true if the command was consumed in the process; Otherwise false, which + * indicates that the command shall be handled by other actions. + */ + abstract boolean processCommand(HdmiCecMessage cmd); + + /** + * Called when the action should handle the timer event it created before. + * + * <p>CEC standard mandates each command transmission should be responded within + * certain period of time. The method is called when the timer it created as it transmitted + * a command gets expired. Inner logic should take an appropriate action. + * + * @param state the state associated with the time when the timer was created + */ + abstract void handleTimerEvent(int state); + + /** + * Timer handler interface used for FeatureAction classes. + */ + interface ActionTimer { + /** + * Send a timer message. + * + * Also carries the state of the action when the timer is created. Later this state is + * compared to the one the action is in when it receives the timer to let the action tell + * the right timer to handle. + * + * @param state state of the action is in + * @param delayMillis amount of delay for the timer + */ + void sendTimerMessage(int state, long delayMillis); + } + + private class ActionTimerHandler extends Handler implements ActionTimer { + + public ActionTimerHandler(Looper looper) { + super(looper); + } + + @Override + public void sendTimerMessage(int state, long delayMillis) { + sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_TIMEOUT: + handleTimerEvent(msg.arg1); + break; + default: + Slog.w(TAG, "Unsupported message:" + msg.what); + break; + } + } + } + + private ActionTimer createActionTimer(Looper looper) { + return new ActionTimerHandler(looper); + } + + // Add a new timer. The timer event will come to mActionTimer.handleMessage() in + // delayMillis. + protected void addTimer(int state, int delayMillis) { + mActionTimer.sendTimerMessage(state, delayMillis); + } + + static HdmiCecMessage buildCommand(int src, int dst, int opcode, byte[] params) { + return new HdmiCecMessage(src, dst, opcode, params); + } + + // Build a CEC command that does not have parameter. + static HdmiCecMessage buildCommand(int src, int dst, int opcode) { + return new HdmiCecMessage(src, dst, opcode, HdmiCecMessage.EMPTY_PARAM); + } + + protected final void sendCommand(HdmiCecMessage cmd) { + mService.sendCecCommand(cmd); + } + + protected final void sendBroadcastCommand(int opcode, byte[] param) { + sendCommand(buildCommand(mSourceAddress, HdmiCec.ADDR_BROADCAST, opcode, param)); + } + + /** + * Finish up the action. Reset the state, and remove itself from the action queue. + */ + protected void finish() { + mState = STATE_NONE; + removeAction(this); + } + + /** + * Remove the action from the action queue. This is called after the action finishes + * its role. + * + * @param action + */ + private void removeAction(FeatureAction action) { + mService.removeAction(action); + } + + // Utility methods for generating parameter byte arrays for CEC commands. + protected static byte[] uiCommandParam(int uiCommand) { + return new byte[] {(byte) uiCommand}; + } + + protected static byte[] physicalAddressParam(int physicalAddress) { + return new byte[] { + (byte) ((physicalAddress >> 8) & 0xFF), + (byte) (physicalAddress & 0xFF) + }; + } + + protected static byte[] pathPairParam(int oldPath, int newPath) { + return new byte[] { + (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF), + (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF) + }; + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 5f07108..c87fc99 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -16,9 +16,20 @@ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.util.Slog; +import android.util.SparseArray; + +import libcore.util.EmptyArray; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command @@ -32,17 +43,48 @@ import android.os.Message; class HdmiCecController { private static final String TAG = "HdmiCecController"; + private static final byte[] EMPTY_BODY = EmptyArray.BYTE; + + // A message to pass cec send command to IO looper. + private static final int MSG_SEND_CEC_COMMAND = 1; + // A message to delegate logical allocation to IO looper. + private static final int MSG_ALLOCATE_LOGICAL_ADDRESS = 2; + + // Message types to handle incoming message in main service looper. + private final static int MSG_RECEIVE_CEC_COMMAND = 1; + // A message to report allocated logical address to main control looper. + private final static int MSG_REPORT_LOGICAL_ADDRESS = 2; + + // TODO: move these values to HdmiCec.java once make it internal constant class. + // CEC's ABORT reason values. + private static final int ABORT_UNRECOGNIZED_MODE = 0; + private static final int ABORT_NOT_IN_CORRECT_MODE = 1; + private static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; + private static final int ABORT_INVALID_OPERAND = 3; + private static final int ABORT_REFUSED = 4; + private static final int ABORT_UNABLE_TO_DETERMINE = 5; + + private static final int NUM_LOGICAL_ADDRESS = 16; + + // TODO: define other constants for errors. + private static final int ERROR_SUCCESS = 0; + // Handler instance to process synchronous I/O (mainly send) message. private Handler mIoHandler; // Handler instance to process various messages coming from other CEC // device or issued by internal state change. - private Handler mMessageHandler; + private Handler mControlHandler; // Stores the pointer to the native implementation of the service that // interacts with HAL. private long mNativePtr; + // Map-like container of all cec devices. A logical address of device is + // used as key of container. + private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = + new SparseArray<HdmiCecDeviceInfo>(); + // Private constructor. Use HdmiCecController.create(). private HdmiCecController() { } @@ -52,14 +94,12 @@ class HdmiCecController { * inner device or has no device it will return {@code null}. * * <p>Declared as package-private, accessed by {@link HdmiControlService} only. - * - * @param ioLooper a Looper instance to handle IO (mainly send message) operation. - * @param messageHandler a message handler that processes a message coming from other - * CEC compatible device or callback of internal state change. + * @param service {@link HdmiControlService} instance used to create internal handler + * and to pass callback for incoming message or event. * @return {@link HdmiCecController} if device is initialized successfully. Otherwise, * returns {@code null}. */ - static HdmiCecController create(Looper ioLooper, Handler messageHandler) { + static HdmiCecController create(HdmiControlService service) { HdmiCecController handler = new HdmiCecController(); long nativePtr = nativeInit(handler); if (nativePtr == 0L) { @@ -67,28 +107,263 @@ class HdmiCecController { return null; } - handler.init(ioLooper, messageHandler, nativePtr); + handler.init(service, nativePtr); return handler; } - private void init(Looper ioLooper, Handler messageHandler, long nativePtr) { - mIoHandler = new Handler(ioLooper) { - @Override - public void handleMessage(Message msg) { - // TODO: Call native sendMessage. + /** + * Interface to report allocated logical address. + */ + interface AllocateLogicalAddressCallback { + /** + * Called when a new logical address is allocated. + * + * @param deviceType requested device type to allocate logical address + * @param logicalAddress allocated logical address. If it is + * {@link HdmiCec#ADDR_UNREGISTERED}, it means that + * it failed to allocate logical address for the given device type + */ + void onAllocated(int deviceType, int logicalAddress); + } + + /** + * Allocate a new logical address of the given device type. Allocated + * address will be reported through {@link AllocateLogicalAddressCallback}. + * + * <p> Declared as package-private, accessed by {@link HdmiControlService} only. + * + * @param deviceType type of device to used to determine logical address + * @param preferredAddress a logical address preferred to be allocated. + * If sets {@link HdmiCec#ADDR_UNREGISTERED}, scans + * the smallest logical address matched with the given device type. + * Otherwise, scan address will start from {@code preferredAddress} + * @param callback callback interface to report allocated logical address to caller + */ + void allocateLogicalAddress(int deviceType, int preferredAddress, + AllocateLogicalAddressCallback callback) { + Message msg = mIoHandler.obtainMessage(MSG_ALLOCATE_LOGICAL_ADDRESS); + msg.arg1 = deviceType; + msg.arg2 = preferredAddress; + msg.obj = callback; + mIoHandler.sendMessage(msg); + } + + private static byte[] buildBody(int opcode, byte[] params) { + byte[] body = new byte[params.length + 1]; + body[0] = (byte) opcode; + System.arraycopy(params, 0, body, 1, params.length); + return body; + } + + private final class IoHandler extends Handler { + private IoHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SEND_CEC_COMMAND: + HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj; + byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams()); + nativeSendCecCommand(mNativePtr, cecMessage.getSource(), + cecMessage.getDestination(), body); + break; + case MSG_ALLOCATE_LOGICAL_ADDRESS: + int deviceType = msg.arg1; + int preferredAddress = msg.arg2; + AllocateLogicalAddressCallback callback = + (AllocateLogicalAddressCallback) msg.obj; + handleAllocateLogicalAddress(deviceType, preferredAddress, callback); + break; + default: + Slog.w(TAG, "Unsupported CEC Io request:" + msg.what); + break; + } + } + + private void handleAllocateLogicalAddress(int deviceType, int preferredAddress, + AllocateLogicalAddressCallback callback) { + int startAddress = preferredAddress; + // If preferred address is "unregistered", start_index will be the smallest + // address matched with the given device type. + if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) { + for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { + if (deviceType == HdmiCec.getTypeFromAddress(i)) { + startAddress = i; + break; + } + } + } + + int logcialAddress = HdmiCec.ADDR_UNREGISTERED; + // Iterates all possible addresses which has the same device type. + for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { + int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; + if (curAddress != HdmiCec.ADDR_UNREGISTERED + && deviceType == HdmiCec.getTypeFromAddress(i)) { + // <Polling Message> is a message which has empty body and + // uses same address for both source and destination address. + // If sending <Polling Message> failed (NAK), it becomes + // new logical address for the device because no device uses + // it as logical address of the device. + int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress, + EMPTY_BODY); + if (error != ERROR_SUCCESS) { + logcialAddress = curAddress; + break; + } + } } - }; - mMessageHandler = messageHandler; + Message msg = mControlHandler.obtainMessage(MSG_REPORT_LOGICAL_ADDRESS); + msg.arg1 = deviceType; + msg.arg2 = logcialAddress; + msg.obj = callback; + mControlHandler.sendMessage(msg); + } + } + + private final class ControlHandler extends Handler { + private ControlHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_RECEIVE_CEC_COMMAND: + // TODO: delegate it to HdmiControl service. + onReceiveCommand((HdmiCecMessage) msg.obj); + break; + case MSG_REPORT_LOGICAL_ADDRESS: + int deviceType = msg.arg1; + int logicalAddress = msg.arg2; + AllocateLogicalAddressCallback callback = + (AllocateLogicalAddressCallback) msg.obj; + callback.onAllocated(deviceType, logicalAddress); + break; + default: + Slog.i(TAG, "Unsupported message type:" + msg.what); + break; + } + } + } + + /** + * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same + * logical address as new device info's. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + * + * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added. + * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo} + * that has the same logical address as new one has. + */ + HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { + HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress()); + if (oldDeviceInfo != null) { + removeDeviceInfo(deviceInfo.getLogicalAddress()); + } + mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo); + return oldDeviceInfo; + } + + /** + * Remove a device info corresponding to the given {@code logicalAddress}. + * It returns removed {@link HdmiCecDeviceInfo} if exists. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + * + * @param logicalAddress logical address of device to be removed + * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null} + */ + HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) { + HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress); + if (deviceInfo != null) { + mDeviceInfos.remove(logicalAddress); + } + return deviceInfo; + } + + /** + * Return a list of all {@HdmiCecDeviceInfo}. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + */ + List<HdmiCecDeviceInfo> getDeviceInfoList() { + List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<HdmiCecDeviceInfo>( + mDeviceInfos.size()); + for (int i = 0; i < mDeviceInfos.size(); ++i) { + deviceInfoList.add(mDeviceInfos.valueAt(i)); + } + return deviceInfoList; + } + + /** + * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + * + * @param logicalAddress logical address to be retrieved + * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}. + * Returns null if no logical address matched + */ + HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { + return mDeviceInfos.get(logicalAddress); + } + + private void init(HdmiControlService service, long nativePtr) { + mIoHandler = new IoHandler(service.getServiceLooper()); + mControlHandler = new ControlHandler(service.getServiceLooper()); mNativePtr = nativePtr; } + private void onReceiveCommand(HdmiCecMessage message) { + // TODO: Handle message according to opcode type. + + // TODO: Use device's source address for broadcast message. + int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ? + message.getDestination() : 0; + // Reply <Feature Abort> to initiator (source) for all requests. + sendFeatureAbort(sourceAddress, message.getSource(), message.getOpcode(), + ABORT_REFUSED); + } + + private void sendFeatureAbort(int srcAddress, int destAddress, int originalOpcode, + int reason) { + byte[] params = new byte[2]; + params[0] = (byte) originalOpcode; + params[1] = (byte) reason; + + HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, destAddress, + HdmiCec.MESSAGE_FEATURE_ABORT, params); + Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage); + mIoHandler.sendMessage(message); + } + + /** + * Called by native when incoming CEC message arrived. + */ + private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) { + byte opcode = body[0]; + byte params[] = Arrays.copyOfRange(body, 1, body.length); + HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params); + + // Delegate message to main handler so that it handles in main thread. + Message message = mControlHandler.obtainMessage( + MSG_RECEIVE_CEC_COMMAND, cecMessage); + mControlHandler.sendMessage(message); + } + /** - * Called by native when an HDMI-CEC message arrived. + * Called by native when a hotplug event issues. */ - private void handleMessage(int srcAddress, int dstAddres, int opcode, byte[] params) { - // TODO: Translate message and delegate it to main message handler. + private void handleHotplug(boolean connected) { + // TODO: Delegate event to main message handler. } private static native long nativeInit(HdmiCecController handler); + private static native int nativeSendCecCommand(long contollerPtr, int srcAddress, + int dstAddress, byte[] body); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 56c5b49..f99c717 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -18,9 +18,10 @@ package com.android.server.hdmi; import android.annotation.Nullable; import android.content.Context; -import android.os.Handler; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; import android.os.HandlerThread; -import android.os.Message; +import android.os.Looper; import android.util.Slog; import com.android.server.SystemService; @@ -37,14 +38,6 @@ public final class HdmiControlService extends SystemService { // and sparse call it shares a thread to handle IO operations. private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread"); - // Main handler class to handle incoming message from each controller. - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - // TODO: Add handler for each message type. - } - }; - @Nullable private HdmiCecController mCecController; @@ -57,14 +50,70 @@ public final class HdmiControlService extends SystemService { @Override public void onStart() { - mCecController = HdmiCecController.create(mIoThread.getLooper(), mHandler); + mCecController = HdmiCecController.create(this); if (mCecController == null) { Slog.i(TAG, "Device does not support HDMI-CEC."); } - mMhlController = HdmiMhlController.create(mIoThread.getLooper(), mHandler); + mMhlController = HdmiMhlController.create(this); if (mMhlController == null) { Slog.i(TAG, "Device does not support MHL-control."); } } + + /** + * Returns {@link Looper} for IO operation. + * + * <p>Declared as package-private. + */ + Looper getIoLooper() { + return mIoThread.getLooper(); + } + + /** + * Returns {@link Looper} of main thread. Use this {@link Looper} instance + * for tasks that are running on main service thread. + * + * <p>Declared as package-private. + */ + Looper getServiceLooper() { + return Looper.myLooper(); + } + + /** + * Add a new {@link FeatureAction} to the action queue. + * + * @param action {@link FeatureAction} to add + */ + void addAction(FeatureAction action) { + // TODO: Implement this. + } + + + /** + * Remove the given {@link FeatureAction} object from the action queue. + * + * @param action {@link FeatureAction} to add + */ + void removeAction(FeatureAction action) { + // TODO: Implement this. + } + + /** + * Transmit a CEC command to CEC bus. + * + * @param command CEC command to send out + */ + void sendCecCommand(HdmiCecMessage command) { + // TODO: Implement this. + } + + /** + * Add a new {@link HdmiCecDeviceInfo} to controller. + * + * @param deviceInfo new device information object to add + */ + void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { + // TODO: Implement this. + } } diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java new file mode 100644 index 0000000..c84a067 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 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.hdmi; + +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.util.Slog; + +import java.io.UnsupportedEncodingException; + +/** + * Feature action that discovers the information of a newly found logical device. + * + * This action is created when receiving <Report Physical Address>, a CEC command a newly + * connected HDMI-CEC device broadcasts to announce its advent. Additional commands are issued in + * this action to gather more information on the device such as OSD name and device vendor ID. + * + * <p>The result is made in the form of {@link HdmiCecDeviceInfo} object, and passed to service + * for the management through its life cycle. + * + * <p>Package-private, accessed by {@link HdmiControlService} only. + */ +final class NewDeviceAction extends FeatureAction { + + private static final String TAG = "NewDeviceAction"; + + // State in which the action sent <Give OSD Name> and is waiting for <Set OSD Name> + // that contains the name of the device for display on screen. + static final int STATE_WAITING_FOR_SET_OSD_NAME = 1; + + // State in which the action sent <Give Device Vendor ID> and is waiting for + // <Device Vendor ID> that contains the vendor ID of the device. + static final int STATE_WAITING_FOR_DEVICE_VENDOR_ID = 2; + + private final int mDeviceLogicalAddress; + private final int mDevicePhysicalAddress; + + private int mVendorId; + private String mDisplayName; + + /** + * Constructor. + * + * @param service {@link HdmiControlService} instance + * @param sourceAddress logical address to be used as source address + * @param deviceLogicalAddress logical address of the device in interest + * @param devicePhysicalAddress physical address of the device in interest + */ + NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress, + int devicePhysicalAddress) { + super(service, sourceAddress); + mDeviceLogicalAddress = deviceLogicalAddress; + mDevicePhysicalAddress = devicePhysicalAddress; + mVendorId = HdmiCec.UNKNOWN_VENDOR_ID; + } + + @Override + public boolean start() { + sendCommand( + buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GET_OSD_NAME)); + mState = STATE_WAITING_FOR_SET_OSD_NAME; + addTimer(mState, TIMEOUT_MS); + return true; + } + + @Override + public boolean processCommand(HdmiCecMessage cmd) { + // For the logical device in interest, we want two more pieces of information - + // osd name and vendor id. They are requested in sequence. In case we don't + // get the expected responses (either by timeout or by receiving <feature abort> command), + // set them to a default osd name and unknown vendor id respectively. + int opcode = cmd.getOpcode(); + int src = cmd.getSource(); + byte[] params = cmd.getParams(); + + if (mDeviceLogicalAddress != src) { + return false; + } + + if (mState == STATE_WAITING_FOR_SET_OSD_NAME) { + if (opcode == HdmiCec.MESSAGE_SET_OSD_NAME) { + try { + mDisplayName = new String(params, "US-ASCII"); + } catch (UnsupportedEncodingException e) { + Slog.e(TAG, "Failed to get OSD name: " + e.getMessage()); + } + mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; + requestVendorId(); + return true; + } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { + int requestOpcode = params[1]; + if (requestOpcode == HdmiCec.MESSAGE_SET_OSD_NAME) { + mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; + requestVendorId(); + return true; + } + } + } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { + if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { + if (params.length == 3) { + mVendorId = (params[0] << 16) + (params[1] << 8) + params[2]; + } else { + Slog.e(TAG, "Failed to get device vendor ID: "); + } + addDeviceInfo(); + finish(); + return true; + } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { + int requestOpcode = params[1]; + if (requestOpcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { + addDeviceInfo(); + finish(); + return true; + } + } + } + return false; + } + + private void requestVendorId() { + sendCommand(buildCommand(mSourceAddress, mDeviceLogicalAddress, + HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID)); + addTimer(mState, TIMEOUT_MS); + } + + private void addDeviceInfo() { + if (mDisplayName == null) { + mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress); + } + mService.addDeviceInfo(new HdmiCecDeviceInfo( + mDeviceLogicalAddress, mDevicePhysicalAddress, + HdmiCec.getTypeFromAddress(mDeviceLogicalAddress), + mVendorId, mDisplayName)); + } + + @Override + public void handleTimerEvent(int state) { + if (mState == STATE_NONE || mState != state) { + return; + } + if (state == STATE_WAITING_FOR_SET_OSD_NAME) { + // Osd name request timed out. Try vendor id + mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; + requestVendorId(); + } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { + // vendor id timed out. Go ahead creating the device info what we've got so far. + addDeviceInfo(); + finish(); + } + } +} diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java index 79f192d..09f1c56 100644 --- a/services/core/java/com/android/server/location/FlpHardwareProvider.java +++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java @@ -69,6 +69,7 @@ public class FlpHardwareProvider { sSingletonInstance = new FlpHardwareProvider(context); } + nativeInit(); return sSingletonInstance; } @@ -96,6 +97,7 @@ public class FlpHardwareProvider { } public static boolean isSupported() { + nativeInit(); return nativeIsSupported(); } @@ -216,9 +218,9 @@ public class FlpHardwareProvider { // Core members private static native void nativeClassInit(); private static native boolean nativeIsSupported(); + private static native void nativeInit(); // FlpLocationInterface members - private native void nativeInit(); private native int nativeGetBatchSize(); private native void nativeStartBatching(int requestId, FusedBatchOptions options); private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject); @@ -258,12 +260,10 @@ public class FlpHardwareProvider { public static final String GEOFENCING = "Geofencing"; public IFusedLocationHardware getLocationHardware() { - nativeInit(); return mLocationHardware; } public IFusedGeofenceHardware getGeofenceHardware() { - nativeInit(); return mGeofenceHardwareService; } diff --git a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java index d314ea7..c4e2058 100644 --- a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java @@ -36,6 +36,7 @@ import android.os.UserHandle; import android.util.Log; import android.util.Slog; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -55,13 +56,12 @@ public class MediaRouteProviderProxy { private final String mId; private final ComponentName mComponentName; private final int mUserId; - - private Intent mBindIntent; // Interfaces declared in the manifest - private ArrayList<String> mInterfaces; - private ArrayList<RouteConnectionRecord> mConnections = new ArrayList<RouteConnectionRecord>(); - private Handler mHandler = new Handler(); + private final ArrayList<String> mInterfaces = new ArrayList<String>(); + private final ArrayList<RouteConnectionRecord> mConnections = new ArrayList<RouteConnectionRecord>(); + private final Handler mHandler = new Handler(); + private Intent mBindIntent; private IRouteProvider mBinder; private boolean mRunning; private boolean mInterested; @@ -76,7 +76,9 @@ public class MediaRouteProviderProxy { mId = id; mComponentName = component; mUserId = uid; - mInterfaces = interfaces; + if (interfaces != null) { + mInterfaces.addAll(interfaces); + } mBindIntent = new Intent(RouteProviderService.SERVICE_INTERFACE); mBindIntent.setComponent(mComponentName); } @@ -202,7 +204,7 @@ public class MediaRouteProviderProxy { if (connection != null) { RouteConnectionRecord record = new RouteConnectionRecord( - connection); + connection, mComponentName.getPackageName(), mUserId); mConnections.add(record); if (mRouteListener != null) { mRouteListener.onRouteConnected(sessionId, route, request, record); @@ -234,6 +236,19 @@ public class MediaRouteProviderProxy { return mId; } + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + mId + " " + this); + String indent = prefix + " "; + + pw.println(indent + "component=" + mComponentName.toString()); + pw.println(indent + "user id=" + mUserId); + pw.println(indent + "interfaces=" + mInterfaces.toString()); + pw.println(indent + "connections=" + mConnections.toString()); + pw.println(indent + "running=" + mRunning); + pw.println(indent + "interested=" + mInterested); + pw.println(indent + "bound=" + mBound); + } + private void updateBinding() { if (shouldBind()) { bind(); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index ac7f4f3..3dc17fc 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -40,12 +40,14 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.view.KeyEvent; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -226,6 +228,36 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mService.sessionDied(this); } + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + mTag + " " + this); + + final String indent = prefix + " "; + pw.println(indent + "pid=" + mPid); + pw.println(indent + "info=" + mSessionInfo.toString()); + pw.println(indent + "published=" + mIsPublished); + pw.println(indent + "transport controls enabled=" + mTransportPerformerEnabled); + pw.println(indent + "rating type=" + mRatingType); + pw.println(indent + "controllers: " + mControllerCallbacks.size()); + pw.println(indent + "state=" + mPlaybackState.toString()); + pw.println(indent + "metadata:" + getShortMetadataString()); + pw.println(indent + "route requests {"); + int size = mRequests.size(); + for (int i = 0; i < size; i++) { + pw.println(indent + " " + mRequests.get(i).toString()); + } + pw.println(indent + "}"); + pw.println(indent + "route=" + (mRoute == null ? null : mRoute.toString())); + pw.println(indent + "connection=" + (mConnection == null ? null : mConnection.toString())); + pw.println(indent + "params=" + (mRequest == null ? null : mRequest.toString())); + } + + private String getShortMetadataString() { + int fields = mMetadata == null ? 0 : mMetadata.size(); + String title = mMetadata == null ? null : mMetadata + .getString(MediaMetadata.METADATA_KEY_TITLE); + return "size=" + fields + ", title=" + title; + } + private void onDestroy() { mService.destroySession(this); } @@ -301,6 +333,34 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + private PlaybackState getStateWithUpdatedPosition() { + PlaybackState state = mPlaybackState; + long duration = -1; + if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { + duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); + } + PlaybackState result = null; + if (state != null) { + if (state.getState() == PlaybackState.PLAYSTATE_PLAYING + || state.getState() == PlaybackState.PLAYSTATE_FAST_FORWARDING + || state.getState() == PlaybackState.PLAYSTATE_REWINDING) { + long updateTime = state.getLastPositionUpdateTime(); + if (updateTime > 0) { + long position = (long) (state.getRate() + * (SystemClock.elapsedRealtime() - updateTime)) + state.getPosition(); + if (duration >= 0 && position > duration) { + position = duration; + } else if (position < 0) { + position = 0; + } + result = new PlaybackState(state); + result.setState(state.getState(), position, state.getRate()); + } + } + } + return result == null ? state : result; + } + private final RouteConnectionRecord.Listener mConnectionListener = new RouteConnectionRecord.Listener() { @Override @@ -603,7 +663,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public PlaybackState getPlaybackState() { - return mPlaybackState; + return getStateWithUpdatedPosition(); } @Override diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index bc91370..107f6ad 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -16,7 +16,9 @@ package com.android.server.media; +import android.Manifest; import android.content.Context; +import android.content.pm.PackageManager; import android.media.routeprovider.RouteRequest; import android.media.session.ISession; import android.media.session.ISessionCallback; @@ -30,13 +32,17 @@ import android.text.TextUtils; import android.util.Log; import com.android.server.SystemService; +import com.android.server.Watchdog; +import com.android.server.Watchdog.Monitor; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; /** * System implementation of MediaSessionManager */ -public class MediaSessionService extends SystemService { +public class MediaSessionService extends SystemService implements Monitor { private static final String TAG = "MediaSessionService"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -69,6 +75,7 @@ public class MediaSessionService extends SystemService { public void onStart() { publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); mRouteProviderWatcher.start(); + Watchdog.getInstance().addMonitor(this); } /** @@ -114,14 +121,21 @@ public class MediaSessionService extends SystemService { } } + @Override + public void monitor() { + synchronized (mLock) { + // Check for deadlock + } + } + void sessionDied(MediaSessionRecord session) { - synchronized (mSessions) { + synchronized (mLock) { destroySessionLocked(session); } } void destroySession(MediaSessionRecord session) { - synchronized (mSessions) { + synchronized (mLock) { destroySessionLocked(session); } } @@ -160,9 +174,7 @@ public class MediaSessionService extends SystemService { } catch (RemoteException e) { throw new RuntimeException("Media Session owner died prematurely.", e); } - synchronized (mSessions) { - mSessions.add(session); - } + mSessions.add(session); if (DEBUG) { Log.d(TAG, "Created session for package " + packageName + " with tag " + tag); } @@ -259,6 +271,36 @@ public class MediaSessionService extends SystemService { Binder.restoreCallingIdentity(token); } } + + @Override + public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { + if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump MediaSessionService from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + pw.println("MEDIA SESSION SERVICE (dumpsys media_session)"); + pw.println(); + + synchronized (mLock) { + int count = mSessions.size(); + pw.println("Sessions - have " + count + " states:"); + for (int i = 0; i < count; i++) { + MediaSessionRecord record = mSessions.get(i); + pw.println(); + record.dump(pw, ""); + } + pw.println("Providers:"); + count = mProviders.size(); + for (int i = 0; i < count; i++) { + MediaRouteProviderProxy provider = mProviders.get(i); + provider.dump(pw, ""); + } + } + } } } diff --git a/services/core/java/com/android/server/media/RouteConnectionRecord.java b/services/core/java/com/android/server/media/RouteConnectionRecord.java index 8da0f95..90ddf29 100644 --- a/services/core/java/com/android/server/media/RouteConnectionRecord.java +++ b/services/core/java/com/android/server/media/RouteConnectionRecord.java @@ -29,10 +29,14 @@ import android.util.Log; public class RouteConnectionRecord { private static final String TAG = "RouteConnRecord"; private final IRouteConnection mBinder; + private final String mPackageName; + private final int mUid; private Listener mListener; - public RouteConnectionRecord(IRouteConnection binder) { + public RouteConnectionRecord(IRouteConnection binder, String packageName, int uid) { mBinder = binder; + mPackageName = packageName; + mUid = uid; } /** @@ -89,6 +93,12 @@ public class RouteConnectionRecord { } } + @Override + public String toString() { + return "RouteConnection { binder=" + mBinder.toString() + ", package=" + mPackageName + + ", uid=" + mUid + "}"; + } + /** * Listener to receive updates from the provider for this connection. */ diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 855ae23..416a6b1 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -410,7 +410,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void onImportanceChanged(int pid, int uid, int importance) { + public void onProcessStateChanged(int pid, int uid, int procState) { } @Override diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 29af433..d074565 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.Handler; @@ -28,31 +29,31 @@ import android.service.notification.Condition; import android.service.notification.ConditionProviderService; import android.service.notification.IConditionListener; import android.service.notification.IConditionProvider; +import android.service.notification.ZenModeConfig; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.R; -import libcore.util.Objects; - import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; public class ConditionProviders extends ManagedServices { + private static final Condition[] NO_CONDITIONS = new Condition[0]; private final ZenModeHelper mZenModeHelper; private final ArrayMap<IBinder, IConditionListener> mListeners = new ArrayMap<IBinder, IConditionListener>(); - private final ArrayMap<Uri, ManagedServiceInfo> mConditions - = new ArrayMap<Uri, ManagedServiceInfo>(); - - private Uri mCurrentConditionId; + private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>(); public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles, ZenModeHelper zenModeHelper) { super(context, handler, new Object(), userProfiles); mZenModeHelper = zenModeHelper; mZenModeHelper.addCallback(new ZenModeHelperCallback()); + loadZenConfig(); } @Override @@ -71,20 +72,13 @@ public class ConditionProviders extends ManagedServices { public void dump(PrintWriter pw) { super.dump(pw); synchronized(mMutex) { - pw.print(" mCurrentConditionId="); pw.println(mCurrentConditionId); pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):"); for (int i = 0; i < mListeners.size(); i++) { pw.print(" "); pw.println(mListeners.keyAt(i)); } - pw.print(" mConditions("); pw.print(mConditions.size()); pw.println("):"); - for (int i = 0; i < mConditions.size(); i++) { - pw.print(" "); pw.print(mConditions.keyAt(i)); - final ManagedServiceInfo info = mConditions.valueAt(i); - pw.print(" -> "); pw.print(info.component); - if (!mServices.contains(info)) { - pw.print(" (orphan)"); - } - pw.println(); + pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):"); + for (int i = 0; i < mRecords.size(); i++) { + pw.print(" "); pw.println(mRecords.get(i)); } } } @@ -95,23 +89,49 @@ public class ConditionProviders extends ManagedServices { } @Override - protected void onServiceAdded(IInterface service) { - Slog.d(TAG, "onServiceAdded " + service); - final IConditionProvider provider = (IConditionProvider) service; + protected void onServiceAdded(ManagedServiceInfo info) { + Slog.d(TAG, "onServiceAdded " + info); + final IConditionProvider provider = provider(info); try { provider.onConnected(); } catch (RemoteException e) { // we tried } + synchronized (mMutex) { + final int N = mRecords.size(); + for(int i = 0; i < N; i++) { + final ConditionRecord r = mRecords.get(i); + if (!r.component.equals(info.component)) continue; + r.info = info; + // if automatic, auto-subscribe + if (r.isAutomatic) { + try { + final Uri id = r.id; + if (DEBUG) Slog.d(TAG, "Auto-subscribing to configured condition " + id); + provider.onSubscribe(id); + } catch (RemoteException e) { + // we tried + } + } + } + } } @Override protected void onServiceRemovedLocked(ManagedServiceInfo removed) { if (removed == null) return; - for (int i = mConditions.size() - 1; i >= 0; i--) { - if (removed.equals(mConditions.valueAt(i))) { - mConditions.removeAt(i); + for (int i = mRecords.size() - 1; i >= 0; i--) { + final ConditionRecord r = mRecords.get(i); + if (!r.component.equals(removed.component)) continue; + if (r.isManual) { + // removing the current manual condition, exit zen + mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF); + } + if (r.isAutomatic) { + // removing an automatic condition, exit zen + mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF); } + mRecords.remove(i); } } @@ -121,14 +141,15 @@ public class ConditionProviders extends ManagedServices { } } - public void requestZenModeConditions(IConditionListener callback, boolean requested) { + public void requestZenModeConditions(IConditionListener callback, int relevance) { synchronized(mMutex) { if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback - + " requested=" + requested); + + " relevance=" + Condition.relevanceToString(relevance)); if (callback == null) return; - if (requested) { + relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS); + if (relevance != 0) { mListeners.put(callback.asBinder(), callback); - requestConditionsLocked(Condition.FLAG_RELEVANT_NOW); + requestConditionsLocked(relevance); } else { mListeners.remove(callback.asBinder()); if (mListeners.isEmpty()) { @@ -138,25 +159,51 @@ public class ConditionProviders extends ManagedServices { } } + private Condition[] validateConditions(String pkg, Condition[] conditions) { + if (conditions == null || conditions.length == 0) return null; + final int N = conditions.length; + final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N); + for (int i = 0; i < N; i++) { + final Uri id = conditions[i].id; + if (!Condition.isValidId(id, pkg)) { + Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id); + continue; + } + if (valid.containsKey(id)) { + Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id); + continue; + } + valid.put(id, conditions[i]); + } + if (valid.size() == 0) return null; + if (valid.size() == N) return conditions; + final Condition[] rt = new Condition[valid.size()]; + for (int i = 0; i < rt.length; i++) { + rt[i] = valid.valueAt(i); + } + return rt; + } + + private ConditionRecord getRecordLocked(Uri id, ComponentName component) { + final int N = mRecords.size(); + for (int i = 0; i < N; i++) { + final ConditionRecord r = mRecords.get(i); + if (r.id.equals(id) && r.component.equals(component)) { + return r; + } + } + final ConditionRecord r = new ConditionRecord(id, component); + mRecords.add(r); + return r; + } + public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) { synchronized(mMutex) { if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions=" + (conditions == null ? null : Arrays.asList(conditions))); + conditions = validateConditions(pkg, conditions); if (conditions == null || conditions.length == 0) return; final int N = conditions.length; - boolean valid = true; - for (int i = 0; i < N; i++) { - final Uri id = conditions[i].id; - if (!Condition.isValidId(id, pkg)) { - Slog.w(TAG, "Ignoring conditions from " + pkg + " for invalid id: " + id); - valid = false; - } - } - if (!valid) return; - - for (int i = 0; i < N; i++) { - mConditions.put(conditions[i].id, info); - } for (IConditionListener listener : mListeners.values()) { try { listener.onConditionsReceived(conditions); @@ -164,64 +211,156 @@ public class ConditionProviders extends ManagedServices { Slog.w(TAG, "Error sending conditions to listener " + listener, e); } } - if (mCurrentConditionId != null) { - for (int i = 0; i < N; i++) { - final Condition c = conditions[i]; - if (!c.id.equals(mCurrentConditionId)) continue; - if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_ERROR) { - triggerExitLocked(c.state == Condition.STATE_ERROR); - return; + for (int i = 0; i < N; i++) { + final Condition c = conditions[i]; + final ConditionRecord r = getRecordLocked(c.id, info.component); + r.info = info; + r.condition = c; + // if manual, exit zen if false (or failed) + if (r.isManual) { + if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) { + final boolean failed = c.state == Condition.STATE_ERROR; + if (failed) { + Slog.w(TAG, "Exit zen: manual condition failed: " + c); + } else if (DEBUG) { + Slog.d(TAG, "Exit zen: manual condition false: " + c); + } + mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF); + unsubscribeLocked(r); + r.isManual = false; + } + } + // if automatic, exit zen if false (or failed), enter zen if true + if (r.isAutomatic) { + if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) { + final boolean failed = c.state == Condition.STATE_ERROR; + if (failed) { + Slog.w(TAG, "Exit zen: automatic condition failed: " + c); + } else if (DEBUG) { + Slog.d(TAG, "Exit zen: automatic condition false: " + c); + } + mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF); + } else if (c.state == Condition.STATE_TRUE) { + Slog.d(TAG, "Enter zen: automatic condition true: " + c); + mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_ON); } } } } } - private void triggerExitLocked(boolean error) { - if (error) { - Slog.w(TAG, "Zen mode exit condition failed"); - } else if (DEBUG) { - Slog.d(TAG, "Zen mode exit condition triggered"); + public void setZenModeCondition(Uri conditionId) { + if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId); + synchronized(mMutex) { + final int N = mRecords.size(); + for (int i = 0; i < N; i++) { + final ConditionRecord r = mRecords.get(i); + final boolean idEqual = r.id.equals(conditionId); + if (r.isManual && !idEqual) { + // was previous manual condition, unsubscribe + unsubscribeLocked(r); + r.isManual = false; + } else if (idEqual && !r.isManual) { + // is new manual condition, subscribe + subscribeLocked(r); + r.isManual = true; + } + } } - mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF); - unsubscribeLocked(mCurrentConditionId); - mCurrentConditionId = null; } - public void setZenModeCondition(Uri conditionId) { - synchronized(mMutex) { - if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId); - if (Objects.equal(mCurrentConditionId, conditionId)) return; + private void subscribeLocked(ConditionRecord r) { + if (DEBUG) Slog.d(TAG, "subscribeLocked " + r); + final IConditionProvider provider = provider(r); + if (provider == null) { + Slog.w(TAG, "subscribeLocked: no provider"); + return; + } + try { + provider.onSubscribe(r.id); + } catch (RemoteException e) { + Slog.w(TAG, "Error subscribing to " + r, e); + } + } - if (mCurrentConditionId != null) { - unsubscribeLocked(mCurrentConditionId); + private static <T> ArraySet<T> safeSet(T... items) { + final ArraySet<T> rt = new ArraySet<T>(); + if (items == null || items.length == 0) return rt; + final int N = items.length; + for (int i = 0; i < N; i++) { + final T item = items[i]; + if (item != null) { + rt.add(item); } - if (conditionId != null) { - final ManagedServiceInfo info = mConditions.get(conditionId); - final IConditionProvider provider = provider(info); - if (provider == null) return; - try { - provider.onSubscribe(conditionId); - } catch (RemoteException e) { - Slog.w(TAG, "Error subscribing to " + conditionId - + " from " + info.component, e); + } + return rt; + } + + public void setAutomaticZenModeConditions(Uri[] conditionIds) { + setAutomaticZenModeConditions(conditionIds, true /*save*/); + } + + private void setAutomaticZenModeConditions(Uri[] conditionIds, boolean save) { + if (DEBUG) Slog.d(TAG, "setAutomaticZenModeConditions " + + (conditionIds == null ? null : Arrays.asList(conditionIds))); + synchronized(mMutex) { + final ArraySet<Uri> newIds = safeSet(conditionIds); + final int N = mRecords.size(); + boolean changed = false; + for (int i = 0; i < N; i++) { + final ConditionRecord r = mRecords.get(i); + final boolean automatic = newIds.contains(r.id); + if (!r.isAutomatic && automatic) { + // subscribe to new automatic + subscribeLocked(r); + r.isAutomatic = true; + changed = true; + } else if (r.isAutomatic && !automatic) { + // unsubscribe from old automatic + unsubscribeLocked(r); + r.isAutomatic = false; + changed = true; } } - mCurrentConditionId = conditionId; + if (save && changed) { + saveZenConfigLocked(); + } } } - private void unsubscribeLocked(Uri conditionId) { - final ManagedServiceInfo info = mConditions.get(mCurrentConditionId); - final IConditionProvider provider = provider(info); - if (provider == null) return; + public Condition[] getAutomaticZenModeConditions() { + synchronized(mMutex) { + final int N = mRecords.size(); + ArrayList<Condition> rt = null; + for (int i = 0; i < N; i++) { + final ConditionRecord r = mRecords.get(i); + if (r.isAutomatic && r.condition != null) { + if (rt == null) rt = new ArrayList<Condition>(); + rt.add(r.condition); + } + } + return rt == null ? NO_CONDITIONS : rt.toArray(new Condition[rt.size()]); + } + } + + private void unsubscribeLocked(ConditionRecord r) { + if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r); + final IConditionProvider provider = provider(r); + if (provider == null) { + Slog.w(TAG, "unsubscribeLocked: no provider"); + return; + } try { - provider.onUnsubscribe(conditionId); + provider.onUnsubscribe(r.id); } catch (RemoteException e) { - Slog.w(TAG, "Error unsubscribing to " + conditionId + " from " + info.component, e); + Slog.w(TAG, "Error unsubscribing to " + r, e); } } + private static IConditionProvider provider(ConditionRecord r) { + return r == null ? null : provider(r.info); + } + private static IConditionProvider provider(ManagedServiceInfo info) { return info == null ? null : (IConditionProvider) info.service; } @@ -238,20 +377,99 @@ public class ConditionProviders extends ManagedServices { } } + private void loadZenConfig() { + final ZenModeConfig config = mZenModeHelper.getConfig(); + if (config == null) { + if (DEBUG) Slog.d(TAG, "loadZenConfig: no config"); + return; + } + synchronized (mMutex) { + if (config.conditionComponents == null || config.conditionIds == null + || config.conditionComponents.length != config.conditionIds.length) { + if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions"); + setAutomaticZenModeConditions(null, false /*save*/); + return; + } + final ArraySet<Uri> newIds = new ArraySet<Uri>(); + final int N = config.conditionComponents.length; + for (int i = 0; i < N; i++) { + final ComponentName component = config.conditionComponents[i]; + final Uri id = config.conditionIds[i]; + if (component != null && id != null) { + getRecordLocked(id, component); // ensure record exists + newIds.add(id); + } + } + if (DEBUG) Slog.d(TAG, "loadZenConfig: N=" + N); + setAutomaticZenModeConditions(newIds.toArray(new Uri[newIds.size()]), false /*save*/); + } + } + + private void saveZenConfigLocked() { + ZenModeConfig config = mZenModeHelper.getConfig(); + if (config == null) return; + config = config.copy(); + final ArrayList<ConditionRecord> automatic = new ArrayList<ConditionRecord>(); + final int automaticN = mRecords.size(); + for (int i = 0; i < automaticN; i++) { + final ConditionRecord r = mRecords.get(i); + if (r.isAutomatic) { + automatic.add(r); + } + } + if (automatic.isEmpty()) { + config.conditionComponents = null; + config.conditionIds = null; + } else { + final int N = automatic.size(); + config.conditionComponents = new ComponentName[N]; + config.conditionIds = new Uri[N]; + for (int i = 0; i < N; i++) { + final ConditionRecord r = automatic.get(i); + config.conditionComponents[i] = r.component; + config.conditionIds[i] = r.id; + } + } + if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config); + mZenModeHelper.setConfig(config); + } + private class ZenModeHelperCallback extends ZenModeHelper.Callback { @Override + void onConfigChanged() { + loadZenConfig(); + } + + @Override void onZenModeChanged() { final int mode = mZenModeHelper.getZenMode(); if (mode == Global.ZEN_MODE_OFF) { - synchronized (mMutex) { - if (mCurrentConditionId != null) { - if (DEBUG) Slog.d(TAG, "Zen mode off, forcing unsubscribe from " - + mCurrentConditionId); - unsubscribeLocked(mCurrentConditionId); - mCurrentConditionId = null; - } - } + // ensure any manual condition is cleared + setZenModeCondition(null); } } } + + private static class ConditionRecord { + public final Uri id; + public final ComponentName component; + public Condition condition; + public ManagedServiceInfo info; + public boolean isAutomatic; + public boolean isManual; + + private ConditionRecord(Uri id, ComponentName component) { + this.id = id; + this.component = component; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ConditionRecord[id=") + .append(id).append(",component=").append(component); + if (isAutomatic) sb.append(",automatic"); + if (isManual) sb.append(",manual"); + return sb.append(']').toString(); + } + } } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 0621f58..d34b09c 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -101,7 +101,7 @@ abstract public class ManagedServices { abstract protected IInterface asInterface(IBinder binder); - abstract protected void onServiceAdded(IInterface service); + abstract protected void onServiceAdded(ManagedServiceInfo info); protected void onServiceRemovedLocked(ManagedServiceInfo removed) { } @@ -368,11 +368,12 @@ abstract public class ManagedServices { @Override public void onServiceConnected(ComponentName name, IBinder binder) { boolean added = false; + ManagedServiceInfo info = null; synchronized (mMutex) { mServicesBinding.remove(servicesBindingTag); try { mService = asInterface(binder); - ManagedServiceInfo info = newServiceInfo(mService, name, + info = newServiceInfo(mService, name, userid, false /*isSystem*/, this, targetSdkVersion); binder.linkToDeath(info, 0); added = mServices.add(info); @@ -381,7 +382,7 @@ abstract public class ManagedServices { } } if (added) { - onServiceAdded(mService); + onServiceAdded(info); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6e4eb56..6ceee5c 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1355,9 +1355,9 @@ public class NotificationManagerService extends SystemService { } @Override - public void requestZenModeConditions(IConditionListener callback, boolean requested) { + public void requestZenModeConditions(IConditionListener callback, int relevance) { enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); - mConditionProviders.requestZenModeConditions(callback, requested); + mConditionProviders.requestZenModeConditions(callback, relevance); } @Override @@ -1366,6 +1366,18 @@ public class NotificationManagerService extends SystemService { mConditionProviders.setZenModeCondition(conditionId); } + @Override + public void setAutomaticZenModeConditions(Uri[] conditionIds) { + enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); + mConditionProviders.setAutomaticZenModeConditions(conditionIds); + } + + @Override + public Condition[] getAutomaticZenModeConditions() { + enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); + return mConditionProviders.getAutomaticZenModeConditions(); + } + private void enforceSystemOrSystemUI(String message) { if (isCallerSystem()) return; getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, @@ -1600,7 +1612,8 @@ public class NotificationManagerService extends SystemService { // Should this notification make noise, vibe, or use the LED? final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept; - if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept); + if (DBG || intercept) Slog.v(TAG, + "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept); synchronized (mNotificationList) { final StatusBarNotification n = new StatusBarNotification( pkg, opPkg, id, tag, callingUid, callingPid, score, notification, @@ -2320,8 +2333,8 @@ public class NotificationManagerService extends SystemService { } @Override - public void onServiceAdded(IInterface service) { - final INotificationListener listener = (INotificationListener) service; + public void onServiceAdded(ManagedServiceInfo info) { + final INotificationListener listener = (INotificationListener) info.service; final String[] keys = getActiveNotificationKeysFromListener(listener); try { listener.onListenerConnected(keys); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 137730a..154ac96 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -84,6 +84,9 @@ public class ZenModeHelper { "com.google.android.talk", "com.android.mms" )); + private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList( + "com.google.android.deskclock" + )); public ZenModeHelper(Context context, Handler handler) { mContext = context; @@ -122,6 +125,9 @@ public class ZenModeHelper { public boolean shouldIntercept(String pkg, Notification n) { if (mZenMode != Global.ZEN_MODE_OFF) { + if (isAlarm(pkg, n)) { + return false; + } if (isCall(pkg, n)) { return !mConfig.allowCalls; } @@ -223,6 +229,10 @@ public class ZenModeHelper { } } + private boolean isAlarm(String pkg, Notification n) { + return ALARM_PACKAGES.contains(pkg); + } + private boolean isCall(String pkg, Notification n) { return CALL_PACKAGES.contains(pkg); } diff --git a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java b/services/core/java/com/android/server/pm/ForwardingIntentFilter.java new file mode 100644 index 0000000..aba796b --- /dev/null +++ b/services/core/java/com/android/server/pm/ForwardingIntentFilter.java @@ -0,0 +1,102 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import com.android.internal.util.XmlUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; +import android.content.IntentFilter; +import android.util.Log; +import java.io.IOException; +import android.os.UserHandle; + +/** + * The {@link PackageManagerService} maintains some {@link ForwardingIntentFilter}s for every user. + * If an {@link Intent} matches the {@link ForwardingIntentFilter}, then it can be forwarded to the + * {@link #mUserIdDest}. + */ +class ForwardingIntentFilter extends IntentFilter { + private static final String ATTR_USER_ID_DEST = "userIdDest"; + private static final String ATTR_FILTER = "filter"; + + private static final String TAG = "ForwardingIntentFilter"; + + // If the intent matches the IntentFilter, then it can be forwarded to this userId. + final int mUserIdDest; + + ForwardingIntentFilter(IntentFilter filter, int userIdDest) { + super(filter); + mUserIdDest = userIdDest; + } + + public int getUserIdDest() { + return mUserIdDest; + } + + ForwardingIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException { + String userIdDestString = parser.getAttributeValue(null, ATTR_USER_ID_DEST); + if (userIdDestString == null) { + String msg = "Missing element under " + TAG +": " + ATTR_USER_ID_DEST + " at " + + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + mUserIdDest = UserHandle.USER_NULL; + } else { + mUserIdDest = Integer.parseInt(userIdDestString); + } + int outerDepth = parser.getDepth(); + String tagName = parser.getName(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + tagName = parser.getName(); + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } else if (type == XmlPullParser.START_TAG) { + if (tagName.equals(ATTR_FILTER)) { + break; + } else { + String msg = "Unknown element under " + Settings.TAG_FORWARDING_INTENT_FILTERS + + ": " + tagName + " at " + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + } + if (tagName.equals(ATTR_FILTER)) { + readFromXml(parser); + } else { + String msg = "Missing element under " + TAG + ": " + ATTR_FILTER + + " at " + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + + public void writeToXml(XmlSerializer serializer) throws IOException { + serializer.attribute(null, ATTR_USER_ID_DEST, Integer.toString(mUserIdDest)); + serializer.startTag(null, ATTR_FILTER); + super.writeToXml(serializer); + serializer.endTag(null, ATTR_FILTER); + } + + @Override + public String toString() { + return "ForwardingIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this)) + + " " + Integer.toString(mUserIdDest) + "}"; + } +} diff --git a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java b/services/core/java/com/android/server/pm/ForwardingIntentResolver.java new file mode 100644 index 0000000..1616395 --- /dev/null +++ b/services/core/java/com/android/server/pm/ForwardingIntentResolver.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.server.pm; + + +import java.io.PrintWriter; +import com.android.server.IntentResolver; +import java.util.List; + +/** + * Used to find a list of {@link ForwardingIntentFilter}s that match an intent. + */ +class ForwardingIntentResolver + extends IntentResolver<ForwardingIntentFilter, ForwardingIntentFilter> { + @Override + protected ForwardingIntentFilter[] newArray(int size) { + return new ForwardingIntentFilter[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, ForwardingIntentFilter filter) { + return false; + } + + @Override + protected void sortResults(List<ForwardingIntentFilter> results) { + //We don't sort the results + } +} diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index ff816ea..82d3f53 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -211,7 +211,7 @@ public final class Installer extends SystemService { return execute(builder.toString()); } - public int dexopt(String apkPath, int uid, boolean isPublic) { + public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); @@ -219,10 +219,13 @@ public final class Installer extends SystemService { builder.append(uid); builder.append(isPublic ? " 1" : " 0"); builder.append(" *"); // No pkgName arg present + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } - public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName) { + public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, + String instructionSet) { StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); @@ -231,6 +234,8 @@ public final class Installer extends SystemService { builder.append(isPublic ? " 1" : " 0"); builder.append(' '); builder.append(pkgName); + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } @@ -245,19 +250,23 @@ public final class Installer extends SystemService { return execute(builder.toString()); } - public int movedex(String srcPath, String dstPath) { + public int movedex(String srcPath, String dstPath, String instructionSet) { StringBuilder builder = new StringBuilder("movedex"); builder.append(' '); builder.append(srcPath); builder.append(' '); builder.append(dstPath); + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } - public int rmdex(String codePath) { + public int rmdex(String codePath, String instructionSet) { StringBuilder builder = new StringBuilder("rmdex"); builder.append(' '); builder.append(codePath); + builder.append(' '); + builder.append(instructionSet); return execute(builder.toString()); } @@ -344,7 +353,7 @@ public final class Installer extends SystemService { } public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, - String fwdLockApkPath, String asecPath, PackageStats pStats) { + String fwdLockApkPath, String asecPath, String instructionSet, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -358,6 +367,8 @@ public final class Installer extends SystemService { builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); builder.append(' '); builder.append(asecPath != null ? asecPath : "!"); + builder.append(' '); + builder.append(instructionSet); String s = transaction(builder.toString()); String res[] = s.split(" "); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 6030d4d..48e9737 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -16,14 +16,18 @@ package com.android.server.pm; +import android.app.AppGlobals; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ActivityInfo; import android.content.pm.ILauncherApps; import android.content.pm.IOnAppsChangedListener; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.graphics.Rect; @@ -34,6 +38,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.util.Log; import android.util.Slog; import com.android.internal.content.PackageMonitor; @@ -46,7 +51,7 @@ import java.util.List; * managed profiles. */ public class LauncherAppsService extends ILauncherApps.Stub { - + private static final boolean DEBUG = false; private static final String TAG = "LauncherAppsService"; private final Context mContext; private final PackageManager mPm; @@ -69,11 +74,17 @@ public class LauncherAppsService extends ILauncherApps.Stub { @Override public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException { synchronized (mListeners) { + if (DEBUG) { + Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle()); + } if (mListeners.getRegisteredCallbackCount() == 0) { + if (DEBUG) { + Log.d(TAG, "Starting package monitoring"); + } startWatchingPackageBroadcasts(); } mListeners.unregister(listener); - mListeners.register(listener); + mListeners.register(listener, Binder.getCallingUserHandle()); } } @@ -85,6 +96,9 @@ public class LauncherAppsService extends ILauncherApps.Stub { public void removeOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException { synchronized (mListeners) { + if (DEBUG) { + Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle()); + } mListeners.unregister(listener); if (mListeners.getRegisteredCallbackCount() == 0) { stopWatchingPackageBroadcasts(); @@ -103,11 +117,17 @@ public class LauncherAppsService extends ILauncherApps.Stub { * Unregister package broadcast receiver */ private void stopWatchingPackageBroadcasts() { + if (DEBUG) { + Log.d(TAG, "Stopped watching for packages"); + } mPackageMonitor.unregister(); } void checkCallbackCount() { - synchronized (LauncherAppsService.this) { + synchronized (mListeners) { + if (DEBUG) { + Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount()); + } if (mListeners.getRegisteredCallbackCount() == 0) { stopWatchingPackageBroadcasts(); } @@ -170,6 +190,34 @@ public class LauncherAppsService extends ILauncherApps.Stub { } @Override + public boolean isPackageEnabled(String packageName, UserHandle user) + throws RemoteException { + ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); + long ident = Binder.clearCallingIdentity(); + try { + IPackageManager pm = AppGlobals.getPackageManager(); + PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier()); + return info != null && info.applicationInfo.enabled; + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public boolean isActivityEnabled(ComponentName component, UserHandle user) + throws RemoteException { + ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user); + long ident = Binder.clearCallingIdentity(); + try { + IPackageManager pm = AppGlobals.getPackageManager(); + ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier()); + return info != null && info.isEnabled(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public void startActivityAsUser(ComponentName component, Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); @@ -191,13 +239,44 @@ public class LauncherAppsService extends ILauncherApps.Stub { private class MyPackageMonitor extends PackageMonitor { + /** Checks if user is a profile of or same as listeningUser. */ + private boolean isProfileOf(UserHandle user, UserHandle listeningUser, String debugMsg) { + if (user.getIdentifier() == listeningUser.getIdentifier()) { + if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg); + return true; + } + long ident = Binder.clearCallingIdentity(); + try { + UserInfo userInfo = mUm.getUserInfo(user.getIdentifier()); + UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier()); + if (userInfo == null || listeningUserInfo == null + || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID + || userInfo.profileGroupId != listeningUserInfo.profileGroupId) { + if (DEBUG) { + Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":" + + debugMsg); + } + return false; + } else { + if (DEBUG) { + Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":" + + debugMsg); + } + return true; + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override public void onPackageAdded(String packageName, int uid) { UserHandle user = new UserHandle(getChangingUserId()); - // TODO: if (!isProfile(user)) return; final int n = mListeners.beginBroadcast(); for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isProfileOf(user, listeningUser, "onPackageAdded")) continue; try { listener.onPackageAdded(user, packageName); } catch (RemoteException re) { @@ -212,10 +291,11 @@ public class LauncherAppsService extends ILauncherApps.Stub { @Override public void onPackageRemoved(String packageName, int uid) { UserHandle user = new UserHandle(getChangingUserId()); - // TODO: if (!isCurrentProfile(user)) return; final int n = mListeners.beginBroadcast(); for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isProfileOf(user, listeningUser, "onPackageRemoved")) continue; try { listener.onPackageRemoved(user, packageName); } catch (RemoteException re) { @@ -230,10 +310,11 @@ public class LauncherAppsService extends ILauncherApps.Stub { @Override public void onPackageModified(String packageName) { UserHandle user = new UserHandle(getChangingUserId()); - // TODO: if (!isProfile(user)) return; final int n = mListeners.beginBroadcast(); for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isProfileOf(user, listeningUser, "onPackageModified")) continue; try { listener.onPackageChanged(user, packageName); } catch (RemoteException re) { @@ -248,10 +329,11 @@ public class LauncherAppsService extends ILauncherApps.Stub { @Override public void onPackagesAvailable(String[] packages) { UserHandle user = new UserHandle(getChangingUserId()); - // TODO: if (!isProfile(user)) return; final int n = mListeners.beginBroadcast(); for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isProfileOf(user, listeningUser, "onPackagesAvailable")) continue; try { listener.onPackagesAvailable(user, packages, isReplacing()); } catch (RemoteException re) { @@ -266,10 +348,11 @@ public class LauncherAppsService extends ILauncherApps.Stub { @Override public void onPackagesUnavailable(String[] packages) { UserHandle user = new UserHandle(getChangingUserId()); - // TODO: if (!isProfile(user)) return; final int n = mListeners.beginBroadcast(); for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isProfileOf(user, listeningUser, "onPackagesUnavailable")) continue; try { listener.onPackagesUnavailable(user, packages, isReplacing()); } catch (RemoteException re) { @@ -284,7 +367,6 @@ public class LauncherAppsService extends ILauncherApps.Stub { } class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> { - @Override public void onCallbackDied(T callback, Object cookie) { checkCallbackCount(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3111cce..a133d42 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -28,9 +28,12 @@ import static android.system.OsConstants.S_IRGRP; import static android.system.OsConstants.S_IXGRP; import static android.system.OsConstants.S_IROTH; import static android.system.OsConstants.S_IXOTH; +import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER; +import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; +import android.content.pm.PackageParser.*; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; @@ -80,7 +83,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; -import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageStats; import android.content.pm.PackageUserState; import android.content.pm.ParceledListSlice; @@ -145,6 +147,8 @@ import java.io.IOException; import java.io.PrintWriter; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -160,9 +164,11 @@ import java.util.List; import java.util.Map; import java.util.Set; +import dalvik.system.VMRuntime; import libcore.io.IoUtils; import com.android.internal.R; +import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.storage.DeviceStorageMonitorInternal; /** @@ -267,6 +273,8 @@ public class PackageManagerService extends IPackageManager.Stub { static final String mTempContainerPrefix = "smdl2tmp"; + private static String sPreferredInstructionSet; + final ServiceThread mHandlerThread; private static final String IDMAP_PREFIX = "/data/resource-cache/"; @@ -1239,27 +1247,38 @@ public class PackageManagerService extends IPackageManager.Stub { boolean didDexOpt = false; + final List<String> instructionSets = getAllInstructionSets(); + /** * Ensure all external libraries have had dexopt run on them. */ if (mSharedLibraries.size() > 0) { - Iterator<SharedLibraryEntry> libs = mSharedLibraries.values().iterator(); - while (libs.hasNext()) { - String lib = libs.next().path; - if (lib == null) { - continue; - } - try { - if (dalvik.system.DexFile.isDexOptNeededInternal(lib, null, false)) { - alreadyDexOpted.add(lib); - mInstaller.dexopt(lib, Process.SYSTEM_UID, true); - didDexOpt = true; + // NOTE: For now, we're compiling these system "shared libraries" + // (and framework jars) into all available architectures. It's possible + // to compile them only when we come across an app that uses them (there's + // already logic for that in scanPackageLI) but that adds some complexity. + for (String instructionSet : instructionSets) { + for (SharedLibraryEntry libEntry : mSharedLibraries.values()) { + final String lib = libEntry.path; + if (lib == null) { + continue; + } + + try { + if (dalvik.system.DexFile.isDexOptNeededInternal( + lib, null, instructionSet, false)) { + alreadyDexOpted.add(lib); + + // The list of "shared libraries" we have at this point is + mInstaller.dexopt(lib, Process.SYSTEM_UID, true, instructionSet); + didDexOpt = true; + } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Library not found: " + lib); + } catch (IOException e) { + Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? " + + e.getMessage()); } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Library not found: " + lib); - } catch (IOException e) { - Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? " - + e.getMessage()); } } } @@ -1282,49 +1301,37 @@ public class PackageManagerService extends IPackageManager.Stub { */ String[] frameworkFiles = frameworkDir.list(); if (frameworkFiles != null) { - for (int i=0; i<frameworkFiles.length; i++) { - File libPath = new File(frameworkDir, frameworkFiles[i]); - String path = libPath.getPath(); - // Skip the file if we alrady did it. - if (alreadyDexOpted.contains(path)) { - continue; - } - // Skip the file if it is not a type we want to dexopt. - if (!path.endsWith(".apk") && !path.endsWith(".jar")) { - continue; - } - try { - if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, false)) { - mInstaller.dexopt(path, Process.SYSTEM_UID, true); - didDexOpt = true; + // TODO: We could compile these only for the most preferred ABI. We should + // first double check that the dex files for these commands are not referenced + // by other system apps. + for (String instructionSet : instructionSets) { + for (int i=0; i<frameworkFiles.length; i++) { + File libPath = new File(frameworkDir, frameworkFiles[i]); + String path = libPath.getPath(); + // Skip the file if we already did it. + if (alreadyDexOpted.contains(path)) { + continue; + } + // Skip the file if it is not a type we want to dexopt. + if (!path.endsWith(".apk") && !path.endsWith(".jar")) { + continue; + } + try { + if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) { + mInstaller.dexopt(path, Process.SYSTEM_UID, true, instructionSet); + didDexOpt = true; + } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Jar not found: " + path); + } catch (IOException e) { + Slog.w(TAG, "Exception reading jar: " + path, e); } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Jar not found: " + path); - } catch (IOException e) { - Slog.w(TAG, "Exception reading jar: " + path, e); } } } if (didDexOpt) { - File dalvikCacheDir = new File(dataDir, "dalvik-cache"); - - // If we had to do a dexopt of one of the previous - // things, then something on the system has changed. - // Consider this significant, and wipe away all other - // existing dexopt files to ensure we don't leave any - // dangling around. - String[] files = dalvikCacheDir.list(); - if (files != null) { - for (int i=0; i<files.length; i++) { - String fn = files[i]; - if (fn.startsWith("data@app@") - || fn.startsWith("data@app-private@")) { - Slog.i(TAG, "Pruning dalvik file: " + fn); - (new File(dalvikCacheDir, fn)).delete(); - } - } - } + pruneDexFiles(new File(dataDir, "dalvik-cache")); } // Collect vendor overlay packages. @@ -1502,6 +1509,12 @@ public class PackageManagerService extends IPackageManager.Stub { // the correct library paths. updateAllSharedLibrariesLPw(); + + for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) { + adjustCpuAbisForSharedUserLPw(setting.packages, true /* do dexopt */, + false /* force dexopt */, false /* defer dexopt */); + } + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); Slog.i(TAG, "Time to scan packages: " @@ -1551,6 +1564,37 @@ public class PackageManagerService extends IPackageManager.Stub { } // synchronized (mInstallLock) } + private static void pruneDexFiles(File cacheDir) { + // If we had to do a dexopt of one of the previous + // things, then something on the system has changed. + // Consider this significant, and wipe away all other + // existing dexopt files to ensure we don't leave any + // dangling around. + // + // Additionally, delete all dex files from the root directory + // since there shouldn't be any there anyway. + File[] files = cacheDir.listFiles(); + if (files != null) { + for (File file : files) { + if (!file.isDirectory()) { + Slog.i(TAG, "Pruning dalvik file: " + file.getAbsolutePath()); + file.delete(); + } else { + File[] subDirList = file.listFiles(); + if (subDirList != null) { + for (File subDirFile : subDirList) { + final String fn = subDirFile.getName(); + if (fn.startsWith("data@app@") || fn.startsWith("data@app-private@")) { + Slog.i(TAG, "Pruning dalvik file: " + fn); + subDirFile.delete(); + } + } + } + } + } + } + } + public boolean isFirstBoot() { return !mRestoredSettings; } @@ -1840,7 +1884,6 @@ public class PackageManagerService extends IPackageManager.Stub { PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - PackageInfo pi; final PackageSetting ps = (PackageSetting) p.mExtras; if (ps == null) { return null; @@ -2718,6 +2761,59 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.SIGNATURE_NO_MATCH; } + /** + * If the database version for this type of package (internal storage or + * external storage) is less than the version where package signatures + * were updated, return true. + */ + private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) { + return (isExternal(scannedPkg) && mSettings.isExternalDatabaseVersionOlderThan( + DatabaseVersion.SIGNATURE_END_ENTITY)) + || (!isExternal(scannedPkg) && mSettings.isInternalDatabaseVersionOlderThan( + DatabaseVersion.SIGNATURE_END_ENTITY)); + } + + /** + * Used for backward compatibility to make sure any packages with + * certificate chains get upgraded to the new style. {@code existingSigs} + * will be in the old format (since they were stored on disk from before the + * system upgrade) and {@code scannedSigs} will be in the newer format. + */ + private int compareSignaturesCompat(PackageSignatures existingSigs, + PackageParser.Package scannedPkg) { + if (!isCompatSignatureUpdateNeeded(scannedPkg)) { + return PackageManager.SIGNATURE_NO_MATCH; + } + + HashSet<Signature> existingSet = new HashSet<Signature>(); + for (Signature sig : existingSigs.mSignatures) { + existingSet.add(sig); + } + HashSet<Signature> scannedCompatSet = new HashSet<Signature>(); + for (Signature sig : scannedPkg.mSignatures) { + try { + Signature[] chainSignatures = sig.getChainSignatures(); + for (Signature chainSig : chainSignatures) { + scannedCompatSet.add(chainSig); + } + } catch (CertificateEncodingException e) { + scannedCompatSet.add(sig); + } + } + /* + * Make sure the expanded scanned set contains all signatures in the + * existing one. + */ + if (scannedCompatSet.equals(existingSet)) { + // Migrate the old signatures to the new scheme. + existingSigs.assignSignatures(scannedPkg.mSignatures); + // The new KeySets will be re-added later in the scanning process. + mSettings.mKeySetManager.removeAppKeySetData(scannedPkg.packageName); + return PackageManager.SIGNATURE_MATCH; + } + return PackageManager.SIGNATURE_NO_MATCH; + } + public String[] getPackagesForUid(int uid) { uid = UserHandle.getAppId(uid); // reader @@ -3068,6 +3164,33 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + /* + * Returns if intent can be forwarded from the userId from to dest + */ + @Override + public boolean canForwardTo(Intent intent, String resolvedType, int userIdFrom, int userIdDest) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + List<ForwardingIntentFilter> matches = + getMatchingForwardingIntentFilters(intent, resolvedType, userIdFrom); + if (matches != null) { + int size = matches.size(); + for (int i = 0; i < size; i++) { + if (matches.get(i).getUserIdDest() == userIdDest) return true; + } + } + return false; + } + + private List<ForwardingIntentFilter> getMatchingForwardingIntentFilters(Intent intent, + String resolvedType, int userId) { + ForwardingIntentResolver fir = mSettings.mForwardingIntentResolvers.get(userId); + if (fir != null) { + return fir.queryIntent(intent, resolvedType, false, userId); + } + return null; + } + @Override public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { @@ -3096,7 +3219,38 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final String pkgName = intent.getPackage(); if (pkgName == null) { - return mActivities.queryIntent(intent, resolvedType, flags, userId); + List<ResolveInfo> result = + mActivities.queryIntent(intent, resolvedType, flags, userId); + // Checking if we can forward the intent to another user + List<ForwardingIntentFilter> fifs = + getMatchingForwardingIntentFilters(intent, resolvedType, userId); + if (fifs != null) { + ForwardingIntentFilter forwardingIntentFilterWithResult = null; + HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>(); + for (ForwardingIntentFilter fif : fifs) { + int userIdDest = fif.getUserIdDest(); + // Two {@link ForwardingIntentFilter}s can have the same userIdDest and + // match the same an intent. For performance reasons, it is better not to + // run queryIntent twice for the same userId + if (!alreadyTriedUserIds.contains(userIdDest)) { + List<ResolveInfo> resultUser = mActivities.queryIntent(intent, + resolvedType, flags, userIdDest); + if (resultUser != null) { + forwardingIntentFilterWithResult = fif; + // As soon as there is a match in another user, we add the + // intentForwarderActivity to the list of ResolveInfo. + break; + } + alreadyTriedUserIds.add(userIdDest); + } + } + if (forwardingIntentFilterWithResult != null) { + ResolveInfo forwardingResolveInfo = createForwardingResolveInfo( + forwardingIntentFilterWithResult, userId); + result.add(forwardingResolveInfo); + } + } + return result; } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { @@ -3107,6 +3261,28 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private ResolveInfo createForwardingResolveInfo(ForwardingIntentFilter fif, int userIdFrom) { + String className; + int userIdDest = fif.getUserIdDest(); + if (userIdDest == UserHandle.USER_OWNER) { + className = FORWARD_INTENT_TO_USER_OWNER; + } else { + className = FORWARD_INTENT_TO_MANAGED_PROFILE; + } + ComponentName forwardingActivityComponentName = new ComponentName( + mAndroidApplication.packageName, className); + ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, + userIdFrom); + ResolveInfo forwardingResolveInfo = new ResolveInfo(); + forwardingResolveInfo.activityInfo = forwardingActivityInfo; + forwardingResolveInfo.priority = 0; + forwardingResolveInfo.preferredOrder = 0; + forwardingResolveInfo.match = 0; + forwardingResolveInfo.isDefault = true; + forwardingResolveInfo.filter = fif; + return forwardingResolveInfo; + } + @Override public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, @@ -3801,7 +3977,8 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Package pkg, File srcFile, int parseFlags) { if (ps != null && ps.codePath.equals(srcFile) - && ps.timeStamp == srcFile.lastModified()) { + && ps.timeStamp == srcFile.lastModified() + && !isCompatSignatureUpdateNeeded(pkg)) { if (ps.signatures.mSignatures != null && ps.signatures.mSignatures.length != 0) { // Optimization: reuse the existing cached certificates @@ -3912,7 +4089,8 @@ public class PackageManagerService extends IPackageManager.Stub { + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); + ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, + getAppInstructionSetFromSettings(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -3976,7 +4154,8 @@ public class PackageManagerService extends IPackageManager.Stub { + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); + ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, + getAppInstructionSetFromSettings(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -4010,8 +4189,6 @@ public class PackageManagerService extends IPackageManager.Stub { codePath = pkg.mScanPath; // Set application objects path explicitly. setApplicationInfoPaths(pkg, codePath, resPath); - // Applications can run with the primary Cpu Abi unless otherwise is specified - pkg.applicationInfo.requiredCpuAbi = null; // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user); @@ -4051,22 +4228,32 @@ public class PackageManagerService extends IPackageManager.Stub { return processName; } - private boolean verifySignaturesLP(PackageSetting pkgSetting, - PackageParser.Package pkg) { + private boolean verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg) { if (pkgSetting.signatures.mSignatures != null) { // Already existing package. Make sure signatures match - if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures) != - PackageManager.SIGNATURE_MATCH) { - Slog.e(TAG, "Package " + pkg.packageName - + " signatures do not match the previously installed version; ignoring!"); - mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; - return false; - } + boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures) + == PackageManager.SIGNATURE_MATCH; + if (!match) { + match = compareSignaturesCompat(pkgSetting.signatures, pkg) + == PackageManager.SIGNATURE_MATCH; + } + if (!match) { + Slog.e(TAG, "Package " + pkg.packageName + + " signatures do not match the previously installed version; ignoring!"); + mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; + return false; + } } // Check for shared user signatures if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { - if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, - pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { + // Already existing package. Make sure signatures match + boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, + pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; + if (!match) { + match = compareSignaturesCompat(pkgSetting.sharedUser.signatures, pkg) + == PackageManager.SIGNATURE_MATCH; + } + if (!match) { Slog.e(TAG, "Package " + pkg.packageName + " has no signatures that match those in shared user " + pkgSetting.sharedUser.name + "; ignoring!"); @@ -4091,12 +4278,16 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void performBootDexOpt() { - HashSet<PackageParser.Package> pkgs = null; + enforceSystemOrRoot("Only the system can request dexopt be performed"); + + final HashSet<PackageParser.Package> pkgs; synchronized (mPackages) { pkgs = mDeferredDexOpt; mDeferredDexOpt = null; } + if (pkgs != null) { int i = 0; for (PackageParser.Package pkg : pkgs) { @@ -4113,16 +4304,17 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Package p = pkg; synchronized (mInstallLock) { if (!p.mDidDexOpt) { - performDexOptLI(p, false, false, true); + performDexOptLI(p, false /* force dex */, false /* defer */, + true /* include dependencies */); } } } } } + @Override public boolean performDexOpt(String packageName) { enforceSystemOrRoot("Only the system can request dexopt be performed"); - if (!mNoDexOpt) { return false; } @@ -4135,12 +4327,13 @@ public class PackageManagerService extends IPackageManager.Stub { } } synchronized (mInstallLock) { - return performDexOptLI(p, false, false, true) == DEX_OPT_PERFORMED; + return performDexOptLI(p, false /* force dex */, false /* defer */, + true /* include dependencies */) == DEX_OPT_PERFORMED; } } - private void performDexOptLibsLI(ArrayList<String> libs, boolean forceDex, boolean defer, - HashSet<String> done) { + private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, boolean forceDex, + boolean defer, HashSet<String> done) { for (int i=0; i<libs.size(); i++) { PackageParser.Package libPkg; String libName; @@ -4154,7 +4347,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (libPkg != null && !done.contains(libName)) { - performDexOptLI(libPkg, forceDex, defer, done); + performDexOptLI(libPkg, instructionSet, forceDex, defer, done); } } } @@ -4164,24 +4357,29 @@ public class PackageManagerService extends IPackageManager.Stub { static final int DEX_OPT_DEFERRED = 2; static final int DEX_OPT_FAILED = -1; - private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer, - HashSet<String> done) { - boolean performed = false; + private int performDexOptLI(PackageParser.Package pkg, String instructionSetOverride, + boolean forceDex, + boolean defer, HashSet<String> done) { + final String instructionSet = instructionSetOverride != null ? + instructionSetOverride : getAppInstructionSet(pkg.applicationInfo); + if (done != null) { done.add(pkg.packageName); if (pkg.usesLibraries != null) { - performDexOptLibsLI(pkg.usesLibraries, forceDex, defer, done); + performDexOptLibsLI(pkg.usesLibraries, instructionSet, forceDex, defer, done); } if (pkg.usesOptionalLibraries != null) { - performDexOptLibsLI(pkg.usesOptionalLibraries, forceDex, defer, done); + performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSet, forceDex, defer, done); } } + + boolean performed = false; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { String path = pkg.mScanPath; int ret = 0; try { - if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path, pkg.packageName, - defer)) { + if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path, + pkg.packageName, instructionSet, defer)) { if (!forceDex && defer) { if (mDeferredDexOpt == null) { mDeferredDexOpt = new HashSet<PackageParser.Package>(); @@ -4189,10 +4387,12 @@ public class PackageManagerService extends IPackageManager.Stub { mDeferredDexOpt.add(pkg); return DEX_OPT_DEFERRED; } else { - Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); + Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName + + " (instructionSet=" + instructionSet + ")"); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), - pkg.packageName); + pkg.packageName, instructionSet); pkg.mDidDexOpt = true; performed = true; } @@ -4219,17 +4419,58 @@ public class PackageManagerService extends IPackageManager.Stub { return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; } + private String getAppInstructionSet(ApplicationInfo info) { + String instructionSet = getPreferredInstructionSet(); + + if (info.requiredCpuAbi != null) { + instructionSet = VMRuntime.getInstructionSet(info.requiredCpuAbi); + } + + return instructionSet; + } + + private String getAppInstructionSetFromSettings(PackageSetting ps) { + String instructionSet = getPreferredInstructionSet(); + + if (ps.requiredCpuAbiString != null) { + instructionSet = VMRuntime.getInstructionSet(ps.requiredCpuAbiString); + } + + return instructionSet; + } + + private static String getPreferredInstructionSet() { + if (sPreferredInstructionSet == null) { + sPreferredInstructionSet = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]); + } + + return sPreferredInstructionSet; + } + + private static List<String> getAllInstructionSets() { + final String[] allAbis = Build.SUPPORTED_ABIS; + final List<String> allInstructionSets = new ArrayList<String>(allAbis.length); + + for (String abi : allAbis) { + final String instructionSet = VMRuntime.getInstructionSet(abi); + if (!allInstructionSets.contains(instructionSet)) { + allInstructionSets.add(instructionSet); + } + } + + return allInstructionSets; + } + private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer, boolean inclDependencies) { HashSet<String> done; - boolean performed = false; if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) { done = new HashSet<String>(); done.add(pkg.packageName); } else { done = null; } - return performDexOptLI(pkg, forceDex, defer, done); + return performDexOptLI(pkg, null /* instruction set override */, forceDex, defer, done); } private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { @@ -4884,6 +5125,8 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "removed obsolete native libraries for system package " + path); } + + setInternalAppAbi(pkg, pkgSetting); } else { if (!isForwardLocked(pkg) && !isExternal(pkg)) { /* @@ -4915,6 +5158,28 @@ public class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } + } else { + // We don't have to copy the shared libraries if we're in the ASEC container + // but we still need to scan the file to figure out what ABI the app needs. + // + // TODO: This duplicates work done in the default container service. It's possible + // to clean this up but we'll need to change the interface between this service + // and IMediaContainerService (but doing so will spread this logic out, rather + // than centralizing it). + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + if (abi >= 0) { + pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_ABIS[abi]; + } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) { + // Note that (non upgraded) system apps will not have any native + // libraries bundled in their APK, but we're guaranteed not to be + // such an app at this point. + pkg.applicationInfo.requiredCpuAbi = null; + } else { + mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + return null; + } + handle.close(); } if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path); @@ -4991,8 +5256,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if (allowed) { if (!mSharedLibraries.containsKey(name)) { - mSharedLibraries.put(name, new SharedLibraryEntry(null, - pkg.packageName)); + mSharedLibraries.put(name, new SharedLibraryEntry(null, pkg.packageName)); } else if (!name.equals(pkg.packageName)) { Slog.w(TAG, "Package " + pkg.packageName + " library " + name + " already exists; skipping"); @@ -5065,6 +5329,12 @@ public class PackageManagerService extends IPackageManager.Stub { // writer synchronized (mPackages) { + if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) { + // We don't do this here during boot because we can do it all + // at once after scanning all existing packages. + adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, + true, forceDex, (scanMode & SCAN_DEFER_DEX) != 0); + } // We don't expect installation to fail beyond this point, if ((scanMode&SCAN_MONITOR) != 0) { mAppDirs.put(pkg.mPath, pkg); @@ -5409,6 +5679,55 @@ public class PackageManagerService extends IPackageManager.Stub { return pkg; } + public void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser, + boolean doDexOpt, boolean forceDexOpt, boolean deferDexOpt) { + String requiredInstructionSet = null; + PackageSetting requirer = null; + for (PackageSetting ps : packagesForUser) { + if (ps.requiredCpuAbiString != null) { + final String instructionSet = VMRuntime.getInstructionSet(ps.requiredCpuAbiString); + if (requiredInstructionSet != null) { + if (!instructionSet.equals(requiredInstructionSet)) { + // We have a mismatch between instruction sets (say arm vs arm64). + // + // TODO: We should rescan all the packages in a shared UID to check if + // they do contain shared libs for other ABIs in addition to the ones we've + // already extracted. For example, the package might contain both arm64-v8a + // and armeabi-v7a shared libs, and we'd have chosen arm64-v8a on 64 bit + // devices. + String errorMessage = "Instruction set mismatch, " + requirer.pkg.packageName + + " requires " + requiredInstructionSet + " whereas " + ps.pkg.packageName + + " requires " + instructionSet; + Slog.e(TAG, errorMessage); + + reportSettingsProblem(Log.WARN, errorMessage); + // Give up, don't bother making any other changes to the package settings. + return; + } + } else { + requiredInstructionSet = instructionSet; + requirer = ps; + } + } + } + + if (requiredInstructionSet != null) { + for (PackageSetting ps : packagesForUser) { + if (ps.requiredCpuAbiString == null) { + ps.requiredCpuAbiString = requirer.requiredCpuAbiString; + if (ps.pkg != null) { + ps.pkg.applicationInfo.requiredCpuAbi = requirer.requiredCpuAbiString; + Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + ps.requiredCpuAbiString); + if (doDexOpt) { + performDexOptLI(ps.pkg, forceDexOpt, deferDexOpt, true); + mInstaller.rmdex(ps.codePathString, getPreferredInstructionSet()); + } + } + } + } + } + } + private void setUpCustomResolverActivity(PackageParser.Package pkg) { synchronized (mPackages) { mResolverReplaced = true; @@ -5488,6 +5807,31 @@ public class PackageManagerService extends IPackageManager.Stub { pkgSetting.nativeLibraryPathString = nativeLibraryPath; } + // Deduces the required ABI of an upgraded system app. + private void setInternalAppAbi(PackageParser.Package pkg, PackageSetting pkgSetting) { + final String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir); + final String apkName = getApkName(pkg.applicationInfo.sourceDir); + + // This is of the form "/system/lib64/<packagename>", "/vendor/lib64/<packagename>" + // or similar. + final File lib64 = new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath()); + final File lib = new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath()); + + // Assume that the bundled native libraries always correspond to the + // most preferred 32 or 64 bit ABI. + if (lib64.exists()) { + pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + pkgSetting.requiredCpuAbiString = Build.SUPPORTED_64_BIT_ABIS[0]; + } else if (lib.exists()) { + pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + pkgSetting.requiredCpuAbiString = Build.SUPPORTED_32_BIT_ABIS[0]; + } else { + // This is the case where the app has no native code. + pkg.applicationInfo.requiredCpuAbi = null; + pkgSetting.requiredCpuAbiString = null; + } + } + private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir) throws IOException { if (!nativeLibraryDir.isDirectory()) { @@ -8130,7 +8474,8 @@ public class PackageManagerService extends IPackageManager.Stub { int mRet; MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags, - String packageName, String dataDir, int uid, UserHandle user) { + String packageName, String dataDir, String instructionSet, + int uid, UserHandle user) { super(user); this.srcArgs = srcArgs; this.observer = observer; @@ -8139,7 +8484,7 @@ public class PackageManagerService extends IPackageManager.Stub { this.uid = uid; if (srcArgs != null) { Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath())); - targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir); + targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir, instructionSet); } else { targetArgs = null; } @@ -8248,7 +8593,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath, - String nativeLibraryPath) { + String nativeLibraryPath, String instructionSet) { final boolean isInAsec; if (installOnSd(flags)) { /* Apps on SD card are always in ASEC containers. */ @@ -8266,21 +8611,23 @@ public class PackageManagerService extends IPackageManager.Stub { if (isInAsec) { return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, - installOnSd(flags), installForwardLocked(flags)); + instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath); + return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, + instructionSet); } } // Used by package mover - private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir) { + private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir, + String instructionSet) { if (installOnSd(flags) || installForwardLocked(flags)) { String cid = getNextCodePath(packageURI.getPath(), pkgName, "/" + AsecInstallArgs.RES_FILE_NAME); - return new AsecInstallArgs(packageURI, cid, installOnSd(flags), + return new AsecInstallArgs(packageURI, cid, instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(packageURI, pkgName, dataDir); + return new FileInstallArgs(packageURI, pkgName, dataDir, instructionSet); } } @@ -8293,11 +8640,12 @@ public class PackageManagerService extends IPackageManager.Stub { final String installerPackageName; final ManifestDigest manifestDigest; final UserHandle user; + final String instructionSet; InstallArgs(Uri packageURI, IPackageInstallObserver observer, IPackageInstallObserver2 observer2, int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user) { + UserHandle user, String instructionSet) { this.packageURI = packageURI; this.flags = flags; this.observer = observer; @@ -8305,6 +8653,7 @@ public class PackageManagerService extends IPackageManager.Stub { this.installerPackageName = installerPackageName; this.manifestDigest = manifestDigest; this.user = user; + this.instructionSet = instructionSet; } abstract void createCopyFile(); @@ -8360,11 +8709,12 @@ public class PackageManagerService extends IPackageManager.Stub { FileInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.observer2, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser()); + params.getUser(), null /* instruction set */); } - FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) { - super(null, null, null, 0, null, null, null); + FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, + String instructionSet) { + super(null, null, null, 0, null, null, null, instructionSet); File codeFile = new File(fullCodePath); installDir = codeFile.getParentFile(); codeFileName = fullCodePath; @@ -8372,8 +8722,8 @@ public class PackageManagerService extends IPackageManager.Stub { libraryPath = nativeLibraryPath; } - FileInstallArgs(Uri packageURI, String pkgName, String dataDir) { - super(packageURI, null, null, 0, null, null, null); + FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) { + super(packageURI, null, null, 0, null, null, null, instructionSet); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); @@ -8632,7 +8982,10 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceDir = getCodePath(); if (cleanUp()) { - int retCode = mInstaller.rmdex(sourceDir); + if (instructionSet == null) { + throw new IllegalStateException("instructionSet == null"); + } + int retCode = mInstaller.rmdex(sourceDir, instructionSet); if (retCode < 0) { Slog.w(TAG, "Couldn't remove dex file for package: " + " at location " @@ -8696,14 +9049,14 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.observer2, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser()); + params.getUser(), null /* instruction set */); } AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, - boolean isExternal, boolean isForwardLocked) { + String instructionSet, boolean isExternal, boolean isForwardLocked) { super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null); + null, null, null, instructionSet); // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); @@ -8712,18 +9065,19 @@ public class PackageManagerService extends IPackageManager.Stub { setCachePath(subStr1); } - AsecInstallArgs(String cid, boolean isForwardLocked) { + AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) { super(null, null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null); + null, null, null, instructionSet); this.cid = cid; setCachePath(PackageHelper.getSdDir(cid)); } - AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) { + AsecInstallArgs(Uri packageURI, String cid, String instructionSet, + boolean isExternal, boolean isForwardLocked) { super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null); + null, null, null, instructionSet); this.cid = cid; } @@ -8904,7 +9258,10 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceFile = getCodePath(); // Remove dex file - int retCode = mInstaller.rmdex(sourceFile); + if (instructionSet == null) { + throw new IllegalStateException("instructionSet == null"); + } + int retCode = mInstaller.rmdex(sourceFile, instructionSet); if (retCode < 0) { Slog.w(TAG, "Couldn't remove dex file for package: " + " at location " @@ -9289,7 +9646,8 @@ public class PackageManagerService extends IPackageManager.Stub { res.removedInfo.args = createInstallArgs(0, deletedPackage.applicationInfo.sourceDir, deletedPackage.applicationInfo.publicSourceDir, - deletedPackage.applicationInfo.nativeLibraryDir); + deletedPackage.applicationInfo.nativeLibraryDir, + getAppInstructionSet(deletedPackage.applicationInfo)); } else { res.removedInfo.args = null; } @@ -9349,7 +9707,8 @@ public class PackageManagerService extends IPackageManager.Stub { private int moveDexFilesLI(PackageParser.Package newPackage) { int retCode; if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath); + retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath, + getAppInstructionSet(newPackage.applicationInfo)); if (retCode != 0) { if (mNoDexOpt) { /* @@ -10035,7 +10394,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, - ps.resourcePathString, ps.nativeLibraryPathString); + ps.resourcePathString, ps.nativeLibraryPathString, + getAppInstructionSetFromSettings(ps)); } return true; } @@ -10405,9 +10765,10 @@ public class PackageManagerService extends IPackageManager.Stub { boolean dataOnly = false; String libDirPath = null; String asecPath = null; + PackageSetting ps = null; synchronized (mPackages) { p = mPackages.get(packageName); - PackageSetting ps = mSettings.mPackages.get(packageName); + ps = mSettings.mPackages.get(packageName); if(p == null) { dataOnly = true; if((ps == null) || (ps.pkg == null)) { @@ -10438,7 +10799,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath, - publicSrcDir, asecPath, pStats); + publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps), + pStats); if (res < 0) { return false; } @@ -10750,6 +11112,47 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /* + * For filters that are added with this method: + * if an intent for the user whose id is userIdOrig matches the filter, then this intent can + * also be resolved in the user whose id is userIdDest. + */ + @Override + public void addForwardingIntentFilter(IntentFilter filter, int userIdOrig, int userIdDest) { + int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + throw new SecurityException( + "addForwardingIntentFilter can only be run by the system"); + } + if (filter.countActions() == 0) { + Slog.w(TAG, "Cannot set a forwarding intent filter with no filter actions"); + return; + } + synchronized (mPackages) { + mSettings.editForwardingIntentResolverLPw(userIdOrig).addFilter( + new ForwardingIntentFilter(filter, userIdDest)); + mSettings.writePackageRestrictionsLPr(userIdOrig); + } + } + + @Override + public void clearForwardingIntentFilters(int userIdOrig) { + int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + throw new SecurityException( + "clearForwardingIntentFilter can only be run by the system"); + } + synchronized (mPackages) { + ForwardingIntentResolver fir = mSettings.editForwardingIntentResolverLPw(userIdOrig); + HashSet<ForwardingIntentFilter> set = + new HashSet<ForwardingIntentFilter>(fir.filterSet()); + for (ForwardingIntentFilter fif : set) { + fir.removeFilter(fif); + } + mSettings.writePackageRestrictionsLPr(userIdOrig); + } + } + @Override public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) { Intent intent = new Intent(Intent.ACTION_MAIN); @@ -11627,7 +12030,9 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } - final AsecInstallArgs args = new AsecInstallArgs(cid, isForwardLocked(ps)); + final AsecInstallArgs args = new AsecInstallArgs(cid, + getAppInstructionSetFromSettings(ps), + isForwardLocked(ps)); // The package status is changed only if the code path // matches between settings and the container id. if (ps.codePathString != null && ps.codePathString.equals(args.getCodePath())) { @@ -11945,15 +12350,17 @@ public class PackageManagerService extends IPackageManager.Stub { * anyway. */ if (returnCode != PackageManager.MOVE_SUCCEEDED) { - processPendingMove(new MoveParams(null, observer, 0, packageName, + processPendingMove(new MoveParams(null, observer, 0, packageName, null, null, -1, user), returnCode); } else { Message msg = mHandler.obtainMessage(INIT_COPY); + final String instructionSet = getAppInstructionSet(pkg.applicationInfo); InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, - pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir); + pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir, + instructionSet); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, - pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user); + pkg.applicationInfo.dataDir, instructionSet, pkg.applicationInfo.uid, user); msg.obj = mp; mHandler.sendMessage(msg); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index a69940f..599d2a7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -36,6 +36,7 @@ import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import com.android.server.pm.PackageManagerService.DumpState; +import java.util.Collection; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -97,7 +98,7 @@ final class Settings { * Note that care should be taken to make sure all database upgrades are * idempotent. */ - private static final int CURRENT_DATABASE_VERSION = DatabaseVersion.FIRST_VERSION; + private static final int CURRENT_DATABASE_VERSION = DatabaseVersion.SIGNATURE_END_ENTITY; /** * This class contains constants that can be referred to from upgrade code. @@ -109,6 +110,12 @@ final class Settings { * The initial version of the database. */ public static final int FIRST_VERSION = 1; + + /** + * Migrating the Signature array from the entire certificate chain to + * just the signing certificate. + */ + public static final int SIGNATURE_END_ENTITY = 2; } private static final boolean DEBUG_STOPPED = false; @@ -124,6 +131,8 @@ final class Settings { private static final String TAG_PACKAGE = "pkg"; private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES = "persistent-preferred-activities"; + static final String TAG_FORWARDING_INTENT_FILTERS = + "forwarding-intent-filters"; private static final String ATTR_NAME = "name"; private static final String ATTR_USER = "user"; @@ -178,6 +187,10 @@ final class Settings { final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities = new SparseArray<PersistentPreferredIntentResolver>(); + // For every user, it is used to find to which other users the intent can be forwarded. + final SparseArray<ForwardingIntentResolver> mForwardingIntentResolvers = + new SparseArray<ForwardingIntentResolver>(); + final HashMap<String, SharedUserSetting> mSharedUsers = new HashMap<String, SharedUserSetting>(); private final ArrayList<Object> mUserIds = new ArrayList<Object>(); @@ -296,6 +309,11 @@ final class Settings { return s; } + Collection<SharedUserSetting> getAllSharedUsersLPw() { + return mSharedUsers.values(); + } + + boolean disableSystemPackageLPw(String name) { final PackageSetting p = mPackages.get(name); if(p == null) { @@ -825,6 +843,15 @@ final class Settings { return ppir; } + ForwardingIntentResolver editForwardingIntentResolverLPw(int userId) { + ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId); + if (fir == null) { + fir = new ForwardingIntentResolver(); + mForwardingIntentResolvers.put(userId, fir); + } + return fir; + } + private File getUserPackagesStateFile(int userId) { return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml"); } @@ -940,6 +967,28 @@ final class Settings { } } + private void readForwardingIntentFiltersLPw(XmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + String tagName = parser.getName(); + if (tagName.equals(TAG_ITEM)) { + ForwardingIntentFilter fif = new ForwardingIntentFilter(parser); + editForwardingIntentResolverLPw(userId).addFilter(fif); + } else { + String msg = "Unknown element under " + TAG_FORWARDING_INTENT_FILTERS + ": " + + parser.getName(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + } + void readPackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Reading package restrictions for user=" + userId); @@ -1068,6 +1117,8 @@ final class Settings { readPreferredActivitiesLPw(parser, userId); } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { readPersistentPreferredActivitiesLPw(parser, userId); + } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) { + readForwardingIntentFiltersLPw(parser, userId); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: " + parser.getName()); @@ -1145,6 +1196,20 @@ final class Settings { serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES); } + void writeForwardingIntentFiltersLPr(XmlSerializer serializer, int userId) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, TAG_FORWARDING_INTENT_FILTERS); + ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId); + if (fir != null) { + for (final ForwardingIntentFilter fif : fir.filterSet()) { + serializer.startTag(null, TAG_ITEM); + fif.writeToXml(serializer); + serializer.endTag(null, TAG_ITEM); + } + } + serializer.endTag(null, TAG_FORWARDING_INTENT_FILTERS); + } + void writePackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Writing package restrictions for user=" + userId); @@ -1243,6 +1308,8 @@ final class Settings { writePersistentPreferredActivitiesLPr(serializer, userId); + writeForwardingIntentFiltersLPr(serializer, userId); + serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS); serializer.endDocument(); @@ -1860,6 +1927,10 @@ final class Settings { // TODO: check whether this is okay! as it is very // similar to how preferred-activities are treated readPersistentPreferredActivitiesLPw(parser, 0); + } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) { + // TODO: check whether this is okay! as it is very + // similar to how preferred-activities are treated + readForwardingIntentFiltersLPw(parser, 0); } else if (tagName.equals("updated-package")) { readDisabledSysPackageLPw(parser); } else if (tagName.equals("cleaning-package")) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index f8103de..3239b46 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -23,7 +23,6 @@ import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.IStopUserCallback; -import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -274,16 +273,6 @@ public class UserManagerService extends IUserManager.Stub { /** Assume permissions already checked and caller's identity cleared */ private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) { - // Getting the service here is not good for testing purposes. - // However, this service is not available when UserManagerService starts - // up so we need a lazy load. - - DevicePolicyManager dpm = null; - if (enabledOnly) { - dpm = (DevicePolicyManager) - mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - } - UserInfo user = getUserInfoLocked(userId); ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); for (int i = 0; i < mUsers.size(); i++) { @@ -291,23 +280,8 @@ public class UserManagerService extends IUserManager.Stub { if (!isProfileOf(user, profile)) { continue; } - - if (enabledOnly && profile.isManagedProfile()) { - if (dpm != null) { - if (!dpm.isProfileEnabled(profile.id)) { - continue; - } - } else { - Log.w(LOG_TAG, - "Attempting to reach DevicePolicyManager before it is started"); - // TODO: There might be system apps that need to call this. - // Make sure that DevicePolicyManagerService is ready at that - // time (otherwise, any default value is a bad one). - throw new IllegalArgumentException(String.format( - "Attempting to get enabled profiles for %d before " - + "DevicePolicyManagerService has been started.", - userId)); - } + if (enabledOnly && !profile.isEnabled()) { + continue; } users.add(profile); } @@ -321,6 +295,18 @@ public class UserManagerService extends IUserManager.Stub { } @Override + public void setUserEnabled(int userId) { + checkManageUsersPermission("enable user"); + synchronized (mPackagesLock) { + UserInfo info = getUserInfoLocked(userId); + if (info != null && !info.isEnabled()) { + info.flags ^= UserInfo.FLAG_DISABLED; + writeUserLocked(info); + } + } + } + + @Override public UserInfo getUserInfo(int userId) { checkManageUsersPermission("query user"); synchronized (mPackagesLock) { @@ -1106,6 +1092,9 @@ public class UserManagerService extends IUserManager.Stub { // on next startup, in case the runtime stops now before stopping and // removing the user completely. user.partial = true; + // Mark it as disabled, so that it isn't returned any more when + // profiles are queried. + user.flags |= UserInfo.FLAG_DISABLED; writeUserLocked(user); } if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); @@ -1134,6 +1123,7 @@ public class UserManagerService extends IUserManager.Stub { // wiping the user's system directory and removing from the user list long ident = Binder.clearCallingIdentity(); try { + final boolean isManaged = getUserInfo(userHandle).isManagedProfile(); Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL, @@ -1154,6 +1144,11 @@ public class UserManagerService extends IUserManager.Stub { removeUserStateLocked(userHandle); } } + // Send broadcast to notify system that the user removed was a + // managed user. + if (isManaged) { + sendProfileRemovedBroadcast(userHandle); + } } }.start(); } @@ -1205,6 +1200,13 @@ public class UserManagerService extends IUserManager.Stub { parent.delete(); } + private void sendProfileRemovedBroadcast(int userHandle) { + Intent managedProfileIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED); + managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(userHandle)); + // Note: This makes an assumption that the parent owner is user 0. + mContext.sendBroadcastAsUser(managedProfileIntent, UserHandle.OWNER, null); + } + @Override public Bundle getApplicationRestrictions(String packageName) { return getApplicationRestrictionsForUser(packageName, UserHandle.getCallingUserId()); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index d84e8e1..d9e95c7 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -409,7 +409,7 @@ final class Notifier { EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0); - mPolicy.screenTurningOn(mScreenOnListener); + mPolicy.wakingUp(mScreenOnListener); mActivityManagerInternal.wakingUp(); if (ActivityManagerNative.isSystemReady()) { @@ -460,7 +460,7 @@ final class Notifier { EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0); - mPolicy.screenTurnedOff(why); + mPolicy.goingToSleep(why); mActivityManagerInternal.goingToSleep(); if (ActivityManagerNative.isSystemReady()) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index cfe24fe..6d2e859 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -496,6 +496,7 @@ public final class PowerManagerService extends com.android.server.SystemService filter = new IntentFilter(); filter.addAction(Intent.ACTION_BOOT_COMPLETED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(new BootCompletedReceiver(), filter, null, mHandler); filter = new IntentFilter(); diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index 486477a..5deb2b8 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -70,8 +70,9 @@ public class SearchManagerService extends ISearchManager.Stub { */ public SearchManagerService(Context context) { mContext = context; - mContext.registerReceiver(new BootCompletedReceiver(), - new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiver(new BootCompletedReceiver(), filter); mContext.registerReceiver(new UserReceiver(), new IntentFilter(Intent.ACTION_USER_REMOVED)); new MyPackageMonitor().register(context, null, UserHandle.ALL, true); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index a2a49c9..9061f96 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -187,7 +187,7 @@ public class TrustManagerService extends SystemService { boolean trustMayHaveChanged = false; for (int i = 0; i < mObsoleteAgents.size(); i++) { - AgentInfo info = mActiveAgents.valueAt(i); + AgentInfo info = mObsoleteAgents.valueAt(i); if (info.agent.isTrusted()) { trustMayHaveChanged = true; } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 649f9dc..50dd27d 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -554,6 +554,10 @@ public final class TvInputManagerService extends SystemService { } } } finally { + if (surface != null) { + // surface is not used in TvInputManagerService. + surface.release(); + } Binder.restoreCallingIdentity(identity); } } diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp index f3e8f3c..527216d 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp @@ -19,57 +19,173 @@ #define LOG_NDEBUG 1 #include "JNIHelp.h" +#include "ScopedPrimitiveArray.h" + +#include <string> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> #include <hardware/hdmi_cec.h> +#include <sys/param.h> namespace android { static struct { - jmethodID handleMessage; + jmethodID handleIncomingCecCommand; + jmethodID handleHotplug; } gHdmiCecControllerClassInfo; - class HdmiCecController { public: - HdmiCecController(jobject callbacksObj); + HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj); + + void init(); + + // Send message to other device. Note that it runs in IO thread. + int sendMessage(const cec_message_t& message); private: + // Propagate the message up to Java layer. + void propagateCecCommand(const cec_message_t& message); + void propagateHotplugEvent(const hotplug_event_t& event); + static void onReceived(const hdmi_event_t* event, void* arg); + static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); + hdmi_cec_device_t* mDevice; jobject mCallbacksObj; }; -HdmiCecController::HdmiCecController(jobject callbacksObj) : +HdmiCecController::HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj) : + mDevice(device), mCallbacksObj(callbacksObj) { } +void HdmiCecController::init() { + mDevice->register_event_callback(mDevice, HdmiCecController::onReceived, this); +} + +void HdmiCecController::propagateCecCommand(const cec_message_t& message) { + jint srcAddr = message.initiator; + jint dstAddr = message.destination; + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jbyteArray body = env->NewByteArray(message.length); + const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body); + env->SetByteArrayRegion(body, 0, message.length, bodyPtr); + + env->CallVoidMethod(mCallbacksObj, + gHdmiCecControllerClassInfo.handleIncomingCecCommand, + srcAddr, dstAddr, body); + env->DeleteLocalRef(body); + + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +void HdmiCecController::propagateHotplugEvent(const hotplug_event_t& event) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, + gHdmiCecControllerClassInfo.handleHotplug, event.connected); + + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +int HdmiCecController::sendMessage(const cec_message_t& message) { + // TODO: propagate send_message's return value. + return mDevice->send_message(mDevice, &message); +} + +// static +void HdmiCecController::checkAndClearExceptionFromCallback(JNIEnv* env, + const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + // static void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) { - HdmiCecController* handler = static_cast<HdmiCecController*>(arg); - if (handler == NULL) { + HdmiCecController* controller = static_cast<HdmiCecController*>(arg); + if (controller == NULL) { return; } - // TODO: propagate message to Java layer. + switch (event->type) { + case HDMI_EVENT_CEC_MESSAGE: + controller->propagateCecCommand(event->cec); + break; + case HDMI_EVENT_HOT_PLUG: + controller->propagateHotplugEvent(event->hotplug); + break; + default: + ALOGE("Unsupported event type: %d", event->type); + break; + } } - //------------------------------------------------------------------------------ +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) { - // TODO: initialize hal and pass it to controller if ready. + int err; + // If use same hardware module id between HdmiCecService and + // HdmiControlSservice it may conflict and cause abnormal state of HAL. + // TODO: use HDMI_CEC_HARDWARE_MODULE_ID of hdmi_cec.h for module id + // once migration to HdmiControlService is done. + hw_module_t* module; + err = hw_get_module("hdmi_cec_module", + const_cast<const hw_module_t **>(&module)); + if (err != 0) { + ALOGE("Error acquiring hardware module: %d", err); + return 0; + } + hw_device_t* device; + // TODO: use HDMI_CEC_HARDWARE_INTERFACE of hdmi_cec.h for interface name + // once migration to HdmiControlService is done. + err = module->methods->open(module, "hdmi_cec_module_hw_if", &device); + if (err != 0) { + ALOGE("Error opening hardware module: %d", err); + return 0; + } HdmiCecController* controller = new HdmiCecController( + reinterpret_cast<hdmi_cec_device*>(device), env->NewGlobalRef(callbacksObj)); + controller->init(); + + GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz, + "handleIncomingCecCommand", "(II[B)V"); + GET_METHOD_ID(gHdmiCecControllerClassInfo.handleHotplug, clazz, + "handleHotplug", "(Z)V"); return reinterpret_cast<jlong>(controller); } +static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, + jint srcAddr, jint dstAddr, jbyteArray body) { + cec_message_t message; + message.initiator = static_cast<cec_logical_address_t>(srcAddr); + message.destination = static_cast<cec_logical_address_t>(dstAddr); + + jsize len = env->GetArrayLength(body); + message.length = MIN(len, CEC_MESSAGE_BODY_MAX_LENGTH); + ScopedByteArrayRO bodyPtr(env, body); + std::memcpy(message.body, bodyPtr.get(), len); + + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->sendMessage(message); +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J", (void *) nativeInit }, + { "nativeSendCecCommand", "(JII[B)I", + (void *) nativeSendCecCommand }, }; #define CLASS_PATH "com/android/server/hdmi/HdmiCecController" diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp index 54c9755..6e03993 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp @@ -67,10 +67,10 @@ public: cec_logical_address_t getLogicalAddress(cec_device_type_t deviceType); uint16_t getPhysicalAddress(); - int getDeviceType(cec_logical_address_t addr); + cec_device_type_t getDeviceType(cec_logical_address_t addr); void queueMessage(const MessageEntry& message); void queueOutgoingMessage(const cec_message_t& message); - void sendReportPhysicalAddress(); + void sendReportPhysicalAddress(cec_logical_address_t srcAddr); void sendActiveSource(cec_logical_address_t srcAddr); void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, int opcode, int reason); @@ -93,15 +93,41 @@ private: EVENT_TYPE_STANDBY }; + /* + * logical address pool for each device type. + */ + static const cec_logical_address_t TV_ADDR_POOL[]; + static const cec_logical_address_t PLAYBACK_ADDR_POOL[]; + static const cec_logical_address_t RECORDER_ADDR_POOL[]; + static const cec_logical_address_t TUNER_ADDR_POOL[]; + static const unsigned int MAX_BUFFER_SIZE = 256; static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF; - static const int INACTIVE_DEVICE_TYPE = -1; static void onReceived(const hdmi_event_t* event, void* arg); static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); void updatePhysicalAddress(); void updateLogicalAddress(); + + // Allocate logical address. The CEC standard recommends that we try to use the address + // we have ever used before, in case this is to allocate an address afte the cable is + // connected again. If preferredAddr is given a valid one (not CEC_ADDR_UNREGISTERED), then + // this method checks if the address is available first. If not, it tries other addresses + // int the address pool available for the given type. + cec_logical_address_t allocateLogicalAddress(cec_device_type_t type, + cec_logical_address_t preferredAddr); + + // Send a CEC ping message. Returns true if successful. + bool sendPing(cec_logical_address_t addr); + + // Return the pool of logical addresses that are used for a given device type. + // One of the addresses in the pool will be chosen in the allocation logic. + bool getLogicalAddressPool(cec_device_type_t type, const cec_logical_address_t** addrPool, + size_t* poolSize); + + // Handles the message retrieved from internal message queue. The message can be + // for either rx or tx. void dispatchMessage(const MessageEntry& message); void processIncomingMessage(const cec_message_t& msg); @@ -159,6 +185,29 @@ private: std::string mOsdName; }; + const cec_logical_address_t HdmiCecHandler::TV_ADDR_POOL[] = { + CEC_ADDR_TV, + CEC_ADDR_FREE_USE, + }; + + const cec_logical_address_t HdmiCecHandler::PLAYBACK_ADDR_POOL[] = { + CEC_ADDR_PLAYBACK_1, + CEC_ADDR_PLAYBACK_2, + CEC_ADDR_PLAYBACK_3 + }; + + const cec_logical_address_t HdmiCecHandler::RECORDER_ADDR_POOL[] = { + CEC_ADDR_RECORDER_1, + CEC_ADDR_RECORDER_2, + CEC_ADDR_RECORDER_3 + }; + + const cec_logical_address_t HdmiCecHandler::TUNER_ADDR_POOL[] = { + CEC_ADDR_TUNER_1, + CEC_ADDR_TUNER_2, + CEC_ADDR_TUNER_3, + CEC_ADDR_TUNER_4 + }; HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) : mDevice(device), @@ -176,39 +225,15 @@ uint16_t HdmiCecHandler::getPhysicalAddress() { return mPhysicalAddress; } -void HdmiCecHandler::updatePhysicalAddress() { - uint16_t addr; - if (!mDevice->get_physical_address(mDevice, &addr)) { - mPhysicalAddress = addr; - } else { - mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; - } -} - -void HdmiCecHandler::updateLogicalAddress() { - std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); - for (; it != mLogicalDevices.end(); ++it) { - cec_logical_address_t addr; - if (!mDevice->get_logical_address(mDevice, it->first, &addr)) { - it->second = addr; - } - } -} - cec_logical_address_t HdmiCecHandler::initLogicalDevice(cec_device_type_t type) { - cec_logical_address_t addr; - int res = mDevice->allocate_logical_address(mDevice, type, &addr); - - if (res != 0) { - ALOGE("Logical Address Allocation failed: %d", res); - } else { - ALOGV("Logical Address Allocation success: %d", addr); + cec_logical_address addr = allocateLogicalAddress(type, CEC_ADDR_UNREGISTERED); + if (addr != CEC_ADDR_UNREGISTERED && !mDevice->add_logical_address(mDevice, addr)) { mLogicalDevices.insert(std::pair<cec_device_type_t, cec_logical_address_t>(type, addr)); // Broadcast <Report Physical Address> when a new logical address was allocated to let // other devices discover the new logical device and its logical - physical address // association. - sendReportPhysicalAddress(); + sendReportPhysicalAddress(addr); } return addr; } @@ -229,14 +254,14 @@ cec_logical_address_t HdmiCecHandler::getLogicalAddress(cec_device_type_t type) return CEC_ADDR_UNREGISTERED; } -int HdmiCecHandler::getDeviceType(cec_logical_address_t addr) { +cec_device_type_t HdmiCecHandler::getDeviceType(cec_logical_address_t addr) { std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); for (; it != mLogicalDevices.end(); ++it) { if (it->second == addr) { return it->first; } } - return INACTIVE_DEVICE_TYPE; + return CEC_DEVICE_INACTIVE; } void HdmiCecHandler::queueMessage(const MessageEntry& entry) { @@ -256,26 +281,26 @@ void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) { queueMessage(entry); } -void HdmiCecHandler::sendReportPhysicalAddress() { +void HdmiCecHandler::sendReportPhysicalAddress(cec_logical_address_t addr) { if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { ALOGE("Invalid physical address."); return; } - - // Report physical address for each logical one hosted in it. - std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); - while (it != mLogicalDevices.end()) { - cec_message_t msg; - msg.initiator = it->second; // logical address - msg.destination = CEC_ADDR_BROADCAST; - msg.length = 4; - msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS; - msg.body[1] = (mPhysicalAddress >> 8) & 0xff; - msg.body[2] = mPhysicalAddress & 0xff; - msg.body[3] = it->first; // device type - queueOutgoingMessage(msg); - ++it; + cec_device_type_t deviceType = getDeviceType(addr); + if (deviceType == CEC_DEVICE_INACTIVE) { + ALOGE("Invalid logical address: %d", addr); + return; } + + cec_message_t msg; + msg.initiator = addr; + msg.destination = CEC_ADDR_BROADCAST; + msg.length = 4; + msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS; + msg.body[1] = (mPhysicalAddress >> 8) & 0xff; + msg.body[2] = mPhysicalAddress & 0xff; + msg.body[3] = deviceType; + queueOutgoingMessage(msg); } void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) { @@ -410,6 +435,99 @@ void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* } } +void HdmiCecHandler::updatePhysicalAddress() { + uint16_t addr; + if (!mDevice->get_physical_address(mDevice, &addr)) { + mPhysicalAddress = addr; + } else { + mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; + } +} + +void HdmiCecHandler::updateLogicalAddress() { + mDevice->clear_logical_address(mDevice); + std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); + for (; it != mLogicalDevices.end(); ++it) { + cec_logical_address_t addr; + cec_logical_address_t preferredAddr = it->second; + cec_device_type_t deviceType = it->first; + addr = allocateLogicalAddress(deviceType, preferredAddr); + if (!mDevice->add_logical_address(mDevice, addr)) { + it->second = addr; + } else { + it->second = CEC_ADDR_UNREGISTERED; + } + } +} + +cec_logical_address_t HdmiCecHandler::allocateLogicalAddress(cec_device_type_t type, + cec_logical_address_t preferredAddr) { + const cec_logical_address_t* addrPool; + size_t poolSize; + if (getLogicalAddressPool(type, &addrPool, &poolSize) < 0) { + return CEC_ADDR_UNREGISTERED; + } + unsigned start = 0; + + // Find the index of preferred address in the pool. If not found, the start + // position will be 0. This happens when the passed preferredAddr is set to + // CEC_ADDR_UNREGISTERED, meaning that no preferred address is given. + for (unsigned i = 0; i < poolSize; i++) { + if (addrPool[i] == preferredAddr) { + start = i; + break; + } + } + for (unsigned i = 0; i < poolSize; i++) { + cec_logical_address_t addr = addrPool[(start + i) % poolSize]; + if (!sendPing(addr)) { + // Failure in pinging means the address is available, not taken by any device. + ALOGV("Logical Address Allocation success: %d", addr); + return addr; + } + } + ALOGE("Logical Address Allocation failed"); + return CEC_ADDR_UNREGISTERED; +} + +bool HdmiCecHandler::sendPing(cec_logical_address addr) { + cec_message_t msg; + msg.initiator = msg.destination = addr; + msg.length = 0; + return !mDevice->send_message(mDevice, &msg); + +} + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +bool HdmiCecHandler::getLogicalAddressPool(cec_device_type_t deviceType, + const cec_logical_address_t** addrPool, size_t* poolSize) { + switch (deviceType) { + case CEC_DEVICE_TV: + *addrPool = TV_ADDR_POOL; + *poolSize = ARRAY_SIZE(TV_ADDR_POOL); + break; + case CEC_DEVICE_RECORDER: + *addrPool = RECORDER_ADDR_POOL; + *poolSize = ARRAY_SIZE(RECORDER_ADDR_POOL); + break; + case CEC_DEVICE_TUNER: + *addrPool = TUNER_ADDR_POOL; + *poolSize = ARRAY_SIZE(TUNER_ADDR_POOL); + break; + case CEC_DEVICE_PLAYBACK: + *addrPool = PLAYBACK_ADDR_POOL; + *poolSize = ARRAY_SIZE(PLAYBACK_ADDR_POOL); + break; + default: + ALOGE("Unsupported device type: %d", deviceType); + return false; + } + return true; +} + +#undef ARRAY_SIZE + void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) { int type = entry.first; mMessageQueueLock.unlock(); @@ -434,7 +552,7 @@ void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) { void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) { int opcode = msg.body[0]; if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) { - sendReportPhysicalAddress(); + sendReportPhysicalAddress(msg.destination); } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) { handleRequestActiveSource(); } else if (opcode == CEC_MESSAGE_GET_OSD_NAME) { @@ -507,7 +625,7 @@ void HdmiCecHandler::handleRequestActiveSource() { JNIEnv* env = AndroidRuntime::getJNIEnv(); jint activeDeviceType = env->CallIntMethod(mCallbacksObj, gHdmiCecServiceClassInfo.getActiveSource); - if (activeDeviceType != INACTIVE_DEVICE_TYPE) { + if (activeDeviceType != CEC_DEVICE_INACTIVE) { sendActiveSource(getLogicalAddress(static_cast<cec_device_type_t>(activeDeviceType))); } checkAndClearExceptionFromCallback(env, __FUNCTION__); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 4085991..34ae8b4 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -870,7 +870,7 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) { if (policyFlags & POLICY_FLAG_INTERACTIVE) { policyFlags |= POLICY_FLAG_PASS_TO_USER; - } else if (policyFlags & (POLICY_FLAG_WAKE | POLICY_FLAG_WAKE_DROPPED)) { + } else if (policyFlags & POLICY_FLAG_WAKE) { JNIEnv* env = jniEnv(); jint wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptWakeMotionBeforeQueueing, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java index 3c46e40..1647425 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java @@ -53,7 +53,6 @@ public class DeviceOwner { private static final String ATTR_NAME = "name"; private static final String ATTR_PACKAGE = "package"; private static final String ATTR_USERID = "userId"; - private static final String ATTR_ENABLED = "profileEnabled"; private AtomicFile fileForWriting; @@ -104,8 +103,7 @@ public class DeviceOwner { */ static DeviceOwner createWithProfileOwner(String packageName, String ownerName, int userId) { DeviceOwner owner = new DeviceOwner(); - owner.mProfileOwners.put( - userId, new OwnerInfo(ownerName, packageName, false /* disabled */)); + owner.mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName)); return owner; } @@ -122,7 +120,7 @@ public class DeviceOwner { } void setProfileOwner(String packageName, String ownerName, int userId) { - mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName, false /* disabled */)); + mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName)); } void removeProfileOwner(int userId) { @@ -139,19 +137,6 @@ public class DeviceOwner { return profileOwner != null ? profileOwner.name : null; } - boolean isProfileEnabled(int userId) { - OwnerInfo profileOwner = mProfileOwners.get(userId); - return profileOwner != null ? profileOwner.enabled : true; - } - - void setProfileEnabled(int userId) { - OwnerInfo profileOwner = mProfileOwners.get(userId); - if (profileOwner == null) { - throw new IllegalArgumentException("No profile owner exists."); - } - profileOwner.enabled = true; - } - boolean hasDeviceOwner() { return mDeviceOwner != null; } @@ -203,12 +188,9 @@ public class DeviceOwner { } else if (tag.equals(TAG_PROFILE_OWNER)) { String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE); String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME); - Boolean profileEnabled = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_ENABLED)); int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID)); mProfileOwners.put(userId, - new OwnerInfo( - profileOwnerName, profileOwnerPackageName, profileEnabled)); + new OwnerInfo(profileOwnerName, profileOwnerPackageName)); } else { throw new XmlPullParserException( "Unexpected tag in device owner file: " + tag); @@ -251,7 +233,6 @@ public class DeviceOwner { out.startTag(null, TAG_PROFILE_OWNER); out.attribute(null, ATTR_PACKAGE, owner.getValue().packageName); out.attribute(null, ATTR_NAME, owner.getValue().name); - out.attribute(null, ATTR_ENABLED, String.valueOf(owner.getValue().enabled)); out.attribute(null, ATTR_USERID, Integer.toString(owner.getKey())); out.endTag(null, TAG_PROFILE_OWNER); } @@ -292,12 +273,6 @@ public class DeviceOwner { static class OwnerInfo { public String name; public String packageName; - public boolean enabled = true; // only makes sense for managed profiles - - public OwnerInfo(String name, String packageName, boolean enabled) { - this(name, packageName); - this.enabled = enabled; - } public OwnerInfo(String name, String packageName) { this.name = name; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index f1ee280..d0a6db1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -588,6 +588,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(KeyChain.ACTION_STORAGE_CHANGED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); @@ -2903,11 +2904,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = UserHandle.getCallingUserId(); Slog.d(LOG_TAG, "Enabling the profile for: " + userId); - mDeviceOwner.setProfileEnabled(userId); - mDeviceOwner.writeOwnerFile(); - + UserManager um = UserManager.get(mContext); long id = Binder.clearCallingIdentity(); try { + um.setUserEnabled(userId); Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED); intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId())); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | @@ -2948,23 +2948,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } - @Override - public boolean isProfileEnabled(int userHandle) { - if (!mHasFeature) { - // If device policy management is not enabled, then the userHandle cannot belong to a - // managed profile. All other profiles are considered enabled. - return true; - } - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); - - synchronized (this) { - if (mDeviceOwner != null) { - return mDeviceOwner.isProfileEnabled(userHandle); - } - } - return true; - } - private boolean isDeviceProvisioned() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) > 0; @@ -3099,6 +3082,51 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public void forwardMatchingIntents(ComponentName who, IntentFilter filter, int flags) { + int callingUserId = UserHandle.getCallingUserId(); + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + IPackageManager pm = AppGlobals.getPackageManager(); + long id = Binder.clearCallingIdentity(); + try { + if ((flags & DevicePolicyManager.FLAG_TO_PRIMARY_USER) != 0) { + pm.addForwardingIntentFilter(filter, callingUserId, UserHandle.USER_OWNER); + } + if ((flags & DevicePolicyManager.FLAG_TO_MANAGED_PROFILE) != 0) { + pm.addForwardingIntentFilter(filter, UserHandle.USER_OWNER, callingUserId); + } + } catch (RemoteException re) { + // Shouldn't happen + } finally { + restoreCallingIdentity(id); + } + } + } + + public void clearForwardingIntentFilters(ComponentName who) { + int callingUserId = UserHandle.getCallingUserId(); + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + IPackageManager pm = AppGlobals.getPackageManager(); + long id = Binder.clearCallingIdentity(); + try { + pm.clearForwardingIntentFilters(callingUserId); + pm.clearForwardingIntentFilters(UserHandle.USER_OWNER); + } catch (RemoteException re) { + // Shouldn't happen + } finally { + restoreCallingIdentity(id); + } + } + } + @Override public Bundle getApplicationRestrictions(ComponentName who, String packageName) { final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 3d82027..7c9f7a8 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -923,7 +923,6 @@ public final class SystemServer { } try { - Slog.i(TAG, "MediaSessionService"); mSystemServiceManager.startService(MediaSessionService.class); } catch (Throwable e) { reportWtf("starting MediaSessionService", e); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index f5ac178..6822ee3 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -373,8 +373,9 @@ public class UsbDeviceManager { mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); - mContext.registerReceiver( - mBootCompletedReceiver, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiver(mBootCompletedReceiver, filter); mContext.registerReceiver( mUserSwitchedReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); } catch (Exception e) { |