summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetService.java5
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java1
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java50
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java92
-rw-r--r--services/core/java/com/android/server/RecognitionManagerService.java4
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java386
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java7
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java72
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java54
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java14
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/am/PendingThumbnailsRecord.java39
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java7
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java11
-rw-r--r--services/core/java/com/android/server/am/UsageStatsService.java262
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java1
-rw-r--r--services/core/java/com/android/server/hdmi/FeatureAction.java211
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java309
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java73
-rw-r--r--services/core/java/com/android/server/hdmi/NewDeviceAction.java165
-rw-r--r--services/core/java/com/android/server/location/FlpHardwareProvider.java6
-rw-r--r--services/core/java/com/android/server/media/MediaRouteProviderProxy.java29
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java62
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java54
-rw-r--r--services/core/java/com/android/server/media/RouteConnectionRecord.java12
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java2
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java386
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java7
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java23
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/ForwardingIntentFilter.java102
-rw-r--r--services/core/java/com/android/server/pm/ForwardingIntentResolver.java44
-rw-r--r--services/core/java/com/android/server/pm/Installer.java21
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java100
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java655
-rw-r--r--services/core/java/com/android/server/pm/Settings.java73
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java58
-rw-r--r--services/core/java/com/android/server/power/Notifier.java4
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java1
-rw-r--r--services/core/java/com/android/server/search/SearchManagerService.java5
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java2
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java4
-rw-r--r--services/core/jni/com_android_server_hdmi_HdmiCecController.cpp134
-rw-r--r--services/core/jni/com_android_server_hdmi_HdmiCecService.cpp216
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java31
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java68
-rw-r--r--services/java/com/android/server/SystemServer.java1
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java5
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 &lt;Report Physical Address&gt;, 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) {