summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/Android.mk1
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java9
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java78
-rw-r--r--services/core/java/com/android/server/DockObserver.java183
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java133
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java5
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java21
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java183
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java2
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java81
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java6
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java2
-rw-r--r--services/core/java/com/android/server/content/ContentService.java2
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java140
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java75
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java13
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java18
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java67
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiConstants.java47
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java310
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiUtils.java74
-rw-r--r--services/core/java/com/android/server/hdmi/HotplugDetectionAction.java193
-rw-r--r--services/core/java/com/android/server/hdmi/RequestArcAction.java21
-rw-r--r--services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java13
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioAction.java192
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java69
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java50
-rw-r--r--services/core/java/com/android/server/location/FlpHardwareProvider.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java2
-rw-r--r--services/core/java/com/android/server/notification/ValidateNotificationPeople.java5
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileIntentFilter.java (renamed from services/core/java/com/android/server/pm/ForwardingIntentFilter.java)44
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileIntentResolver.java (renamed from services/core/java/com/android/server/pm/ForwardingIntentResolver.java)14
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java587
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java65
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java279
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java119
-rw-r--r--services/core/java/com/android/server/pm/Settings.java73
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java104
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java7
-rw-r--r--services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java24
-rw-r--r--services/core/java/com/android/server/wm/CircularDisplayMask.java51
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java4
-rw-r--r--services/core/jni/com_android_server_AssetAtlasService.cpp12
-rw-r--r--services/core/jni/com_android_server_location_FlpHardwareProvider.cpp77
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java165
-rw-r--r--services/java/com/android/server/SystemServer.java35
-rw-r--r--services/restrictions/Android.mk10
-rw-r--r--services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java171
-rw-r--r--services/tests/servicestests/AndroidManifest.xml12
-rw-r--r--services/tests/servicestests/res/xml/device_admin_sample.xml29
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java135
-rw-r--r--services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java152
-rw-r--r--services/usb/java/com/android/server/usb/UsbDebuggingManager.java4
57 files changed, 2911 insertions, 1281 deletions
diff --git a/services/Android.mk b/services/Android.mk
index 5fcef64..b4de903 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -25,6 +25,7 @@ services := \
backup \
devicepolicy \
print \
+ restrictions \
usb \
voiceinteraction
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 87b1d32..7a67d63 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -843,6 +843,15 @@ class AppWidgetServiceImpl {
throw new IllegalArgumentException("Unknown component " + componentName);
}
+ // Ensure that the service specified by the passed intent belongs to the same package
+ // as provides the passed widget id.
+ String widgetIdPackage = id.provider.info.provider.getPackageName();
+ String servicePackage = componentName.getPackageName();
+ if (!servicePackage.equals(widgetIdPackage)) {
+ throw new SecurityException("Specified intent doesn't belong to the same package"
+ + " as the provided AppWidget id");
+ }
+
// If there is already a connection made for this service intent, then disconnect from
// that first. (This does not allow multiple connections to the same service under
// the same key)
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 0082b1e..14c15a7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -26,6 +26,7 @@ import android.app.PendingIntent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
import android.app.backup.FullBackup;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
@@ -82,7 +83,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.StringBuilderPrinter;
-import com.android.internal.backup.BackupConstants;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.backup.IObbBackupService;
import com.android.server.AppWidgetBackupBridge;
@@ -2098,7 +2098,7 @@ public class BackupManagerService extends IBackupManager.Stub {
}
mAgentBinder = null;
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
// Sanity check: if the queue is empty we have no work to do.
if (mOriginalQueue.isEmpty()) {
@@ -2121,14 +2121,14 @@ public class BackupManagerService extends IBackupManager.Stub {
EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
// If we haven't stored package manager metadata yet, we must init the transport.
- if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
+ if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
addBackupTrace("initializing transport " + transportName);
resetBackupState(mStateDir); // Just to make sure.
mStatus = mTransport.initializeDevice();
addBackupTrace("transport.initializeDevice() == " + mStatus);
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
} else {
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
@@ -2141,7 +2141,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// directly and use a synthetic BackupRequest. We always run this pass
// because it's cheap and this way we guarantee that we don't get out of
// step even if we're selecting among various transports at run time.
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
mPackageManager, allAgentPackages());
mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
@@ -2149,7 +2149,7 @@ public class BackupManagerService extends IBackupManager.Stub {
addBackupTrace("PMBA invoke: " + mStatus);
}
- if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
// The backend reports that our dataset has been wiped. Note this in
// the event log; the no-success code below will reset the backup
// state as well.
@@ -2158,13 +2158,13 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread", e);
addBackupTrace("Exception in backup thread: " + e);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
} finally {
// If we've succeeded so far, invokeAgentForBackup() will have run the PM
// metadata and its completion/timeout callback will continue the state
// machine chain. If it failed that won't happen; we handle that now.
addBackupTrace("exiting prelim: " + mStatus);
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
// if things went wrong at this point, we need to
// restage everything and try again later.
resetBackupState(mStateDir); // Just to make sure.
@@ -2176,7 +2176,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// Transport has been initialized and the PM metadata submitted successfully
// if that was warranted. Now we process the single next thing in the queue.
void invokeNextAgent() {
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
addBackupTrace("invoke q=" + mQueue.size());
// Sanity check that we have work to do. If not, skip to the end where
@@ -2236,39 +2236,39 @@ public class BackupManagerService extends IBackupManager.Stub {
// done here as long as we're successful so far.
} else {
// Timeout waiting for the agent
- mStatus = BackupConstants.AGENT_ERROR;
+ mStatus = BackupTransport.AGENT_ERROR;
}
} catch (SecurityException ex) {
// Try for the next one.
Slog.d(TAG, "error in bind/backup", ex);
- mStatus = BackupConstants.AGENT_ERROR;
+ mStatus = BackupTransport.AGENT_ERROR;
addBackupTrace("agent SE");
}
} catch (NameNotFoundException e) {
Slog.d(TAG, "Package does not exist; skipping");
addBackupTrace("no such package");
- mStatus = BackupConstants.AGENT_UNKNOWN;
+ mStatus = BackupTransport.AGENT_UNKNOWN;
} finally {
mWakelock.setWorkSource(null);
// If there was an agent error, no timeout/completion handling will occur.
// That means we need to direct to the next state ourselves.
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
BackupState nextState = BackupState.RUNNING_QUEUE;
mAgentBinder = null;
// An agent-level failure means we reenqueue this one agent for
// a later retry, but otherwise proceed normally.
- if (mStatus == BackupConstants.AGENT_ERROR) {
+ if (mStatus == BackupTransport.AGENT_ERROR) {
if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
+ " - restaging");
dataChangedImpl(request.packageName);
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
if (mQueue.isEmpty()) nextState = BackupState.FINAL;
- } else if (mStatus == BackupConstants.AGENT_UNKNOWN) {
+ } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
// Failed lookup of the app, so we couldn't bring up an agent, but
// we're otherwise fine. Just drop it and go on to the next as usual.
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
} else {
// Transport-level failure means we reenqueue everything
revertAndEndBackup();
@@ -2297,7 +2297,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// If everything actually went through and this is the first time we've
// done a backup, we can now record what the current backup dataset token
// is.
- if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) {
+ if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
addBackupTrace("success; recording token");
try {
mCurrentToken = mTransport.getCurrentRestoreSet();
@@ -2314,7 +2314,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// state machine sequence and the wakelock is refcounted.
synchronized (mQueueLock) {
mBackupRunning = false;
- if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
// Make sure we back up everything and perform the one-time init
clearMetadata();
if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
@@ -2395,7 +2395,7 @@ public class BackupManagerService extends IBackupManager.Stub {
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
e.toString());
agentErrorCleanup();
- return BackupConstants.AGENT_ERROR;
+ return BackupTransport.AGENT_ERROR;
}
// At this point the agent is off and running. The next thing to happen will
@@ -2403,7 +2403,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// for transport, or a timeout. Either way the next phase will happen in
// response to the TimeoutHandler interface callbacks.
addBackupTrace("invoke success");
- return BackupConstants.TRANSPORT_OK;
+ return BackupTransport.TRANSPORT_OK;
}
public void failAgent(IBackupAgent agent, String message) {
@@ -2484,11 +2484,11 @@ public class BackupManagerService extends IBackupManager.Stub {
addBackupTrace("operation complete");
ParcelFileDescriptor backupData = null;
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
try {
int size = (int) mBackupDataName.length();
if (size > 0) {
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
backupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
addBackupTrace("sending data to transport");
@@ -2501,7 +2501,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// renaming *all* the output state files (see below) until that happens.
addBackupTrace("data delivered: " + mStatus);
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
addBackupTrace("finishing op on transport");
mStatus = mTransport.finishBackup();
addBackupTrace("finished: " + mStatus);
@@ -2514,7 +2514,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// After successful transport, delete the now-stale data
// and juggle the files so that next time we supply the agent
// with the new state file it just created.
- if (mStatus == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
mBackupDataName.delete();
mNewStateName.renameTo(mSavedStateName);
EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
@@ -2525,7 +2525,7 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (Exception e) {
Slog.e(TAG, "Transport error backing up " + pkgName, e);
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
} finally {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
}
@@ -2533,7 +2533,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// If we encountered an error here it's a transport-level failure. That
// means we need to halt everything and reschedule everything for next time.
final BackupState nextState;
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
revertAndEndBackup();
nextState = BackupState.FINAL;
} else {
@@ -4847,7 +4847,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
// Assume error until we successfully init everything
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
try {
// TODO: Log this before getAvailableRestoreSets, somehow
@@ -4902,7 +4902,7 @@ public class BackupManagerService extends IBackupManager.Stub {
return;
}
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
executeNextState(RestoreState.DOWNLOAD_DATA);
}
@@ -4917,7 +4917,7 @@ public class BackupManagerService extends IBackupManager.Stub {
try {
mStatus = mTransport.startRestore(mToken,
mRestorePackages.toArray(new PackageInfo[0]));
- if (mStatus != BackupConstants.TRANSPORT_OK) {
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Error starting restore operation");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
executeNextState(RestoreState.FINAL);
@@ -4926,7 +4926,7 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
Slog.e(TAG, "Error communicating with transport for restore");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(RestoreState.FINAL);
return;
}
@@ -4941,14 +4941,14 @@ public class BackupManagerService extends IBackupManager.Stub {
if (packageName == null) {
Slog.e(TAG, "Error getting first restore package");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(RestoreState.FINAL);
return;
} else if (packageName.equals("")) {
Slog.i(TAG, "No restore data available");
int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
- mStatus = BackupConstants.TRANSPORT_OK;
+ mStatus = BackupTransport.TRANSPORT_OK;
executeNextState(RestoreState.FINAL);
return;
} else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
@@ -4979,7 +4979,7 @@ public class BackupManagerService extends IBackupManager.Stub {
Slog.e(TAG, "No restore metadata available, so not restoring settings");
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
"Package manager restore metadata missing");
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
executeNextState(RestoreState.FINAL);
return;
@@ -4987,7 +4987,7 @@ public class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
Slog.e(TAG, "Error communicating with transport for restore");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
executeNextState(RestoreState.FINAL);
return;
@@ -5118,7 +5118,7 @@ public class BackupManagerService extends IBackupManager.Stub {
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to fetch restore data from transport");
- mStatus = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(RestoreState.FINAL);
}
}
@@ -5206,7 +5206,7 @@ public class BackupManagerService extends IBackupManager.Stub {
Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
}
- if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) {
+ if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
// Transport-level failure, so we wind everything up and
// terminate the restore operation.
Slog.e(TAG, "Error getting restore data for " + packageName);
@@ -5450,12 +5450,12 @@ public class BackupManagerService extends IBackupManager.Stub {
long startRealtime = SystemClock.elapsedRealtime();
int status = transport.initializeDevice();
- if (status == BackupConstants.TRANSPORT_OK) {
+ if (status == BackupTransport.TRANSPORT_OK) {
status = transport.finishBackup();
}
// Okay, the wipe really happened. Clean up our local bookkeeping.
- if (status == BackupConstants.TRANSPORT_OK) {
+ if (status == BackupTransport.TRANSPORT_OK) {
Slog.i(TAG, "Device init successful");
int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 4a8bf72..af38664 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -19,10 +19,12 @@ package com.android.server;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
@@ -33,62 +35,62 @@ import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
+import java.io.PrintWriter;
/**
- * <p>DockObserver monitors for a docking station.
+ * DockObserver monitors for a docking station.
*/
-final class DockObserver extends UEventObserver {
- private static final String TAG = DockObserver.class.getSimpleName();
+final class DockObserver extends SystemService {
+ private static final String TAG = "DockObserver";
private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
private static final int MSG_DOCK_STATE_CHANGED = 0;
- private final Object mLock = new Object();
+ private final PowerManager mPowerManager;
+ private final PowerManager.WakeLock mWakeLock;
- private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
- private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private final Object mLock = new Object();
private boolean mSystemReady;
- private final Context mContext;
- private final PowerManager mPowerManager;
- private final PowerManager.WakeLock mWakeLock;
+ private int mActualDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+ private int mReportedDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+ private boolean mUpdatesStopped;
public DockObserver(Context context) {
- mContext = context;
+ super(context);
- mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
init(); // set initial status
- startObserving(DOCK_UEVENT_MATCH);
+
+ mObserver.startObserving(DOCK_UEVENT_MATCH);
}
@Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Dock UEVENT: " + event.toString());
- }
+ public void onStart() {
+ publishBinderService(TAG, new BinderService());
+ }
- synchronized (mLock) {
- try {
- int newState = Integer.parseInt(event.get("SWITCH_STATE"));
- if (newState != mDockState) {
- mPreviousDockState = mDockState;
- mDockState = newState;
- if (mSystemReady) {
- // Wake up immediately when docked or undocked.
- mPowerManager.wakeUp(SystemClock.uptimeMillis());
-
- updateLocked();
- }
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ synchronized (mLock) {
+ mSystemReady = true;
+
+ // don't bother broadcasting undocked here
+ if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ updateLocked();
}
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
}
@@ -100,8 +102,8 @@ final class DockObserver extends UEventObserver {
FileReader file = new FileReader(DOCK_STATE_PATH);
try {
int len = file.read(buffer, 0, 1024);
- mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
- mPreviousDockState = mDockState;
+ setActualDockStateLocked(Integer.valueOf((new String(buffer, 0, len)).trim()));
+ mPreviousDockState = mActualDockState;
} finally {
file.close();
}
@@ -113,13 +115,21 @@ final class DockObserver extends UEventObserver {
}
}
- void systemReady() {
- synchronized (mLock) {
- // don't bother broadcasting undocked here
- if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ private void setActualDockStateLocked(int newState) {
+ mActualDockState = newState;
+ if (!mUpdatesStopped) {
+ setDockStateLocked(newState);
+ }
+ }
+
+ private void setDockStateLocked(int newState) {
+ if (newState != mReportedDockState) {
+ mReportedDockState = newState;
+ if (mSystemReady) {
+ // Wake up immediately when docked or undocked.
+ mPowerManager.wakeUp(SystemClock.uptimeMillis());
updateLocked();
}
- mSystemReady = true;
}
}
@@ -130,10 +140,13 @@ final class DockObserver extends UEventObserver {
private void handleDockStateChange() {
synchronized (mLock) {
- Slog.i(TAG, "Dock state changed: " + mDockState);
+ Slog.i(TAG, "Dock state changed from " + mPreviousDockState + " to "
+ + mReportedDockState);
+ final int previousDockState = mPreviousDockState;
+ mPreviousDockState = mReportedDockState;
// Skip the dock intent if not yet provisioned.
- final ContentResolver cr = mContext.getContentResolver();
+ final ContentResolver cr = getContext().getContentResolver();
if (Settings.Global.getInt(cr,
Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
@@ -143,27 +156,27 @@ final class DockObserver extends UEventObserver {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
+ intent.putExtra(Intent.EXTRA_DOCK_STATE, mReportedDockState);
// Play a sound to provide feedback to confirm dock connection.
// Particularly useful for flaky contact pins...
if (Settings.Global.getInt(cr,
Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1) {
String whichSound = null;
- if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
- (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
- (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
+ if (mReportedDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ if ((previousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (previousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (previousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.Global.DESK_UNDOCK_SOUND;
- } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
+ } else if (previousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.Global.CAR_UNDOCK_SOUND;
}
} else {
- if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
- (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
- (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
+ if ((mReportedDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (mReportedDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (mReportedDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.Global.DESK_DOCK_SOUND;
- } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
+ } else if (mReportedDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.Global.CAR_DOCK_SOUND;
}
}
@@ -173,7 +186,8 @@ final class DockObserver extends UEventObserver {
if (soundPath != null) {
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+ final Ringtone sfx = RingtoneManager.getRingtone(
+ getContext(), soundUri);
if (sfx != null) {
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
sfx.play();
@@ -186,7 +200,7 @@ final class DockObserver extends UEventObserver {
// Send the dock event intent.
// There are many components in the system watching for this so as to
// adjust audio routing, screen orientation, etc.
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
// Release the wake lock that was acquired when the message was posted.
mWakeLock.release();
@@ -203,4 +217,71 @@ final class DockObserver extends UEventObserver {
}
}
};
+
+ private final UEventObserver mObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Dock UEVENT: " + event.toString());
+ }
+
+ try {
+ synchronized (mLock) {
+ setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse switch state from event " + event);
+ }
+ }
+ };
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump dock observer service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
+ pw.println("Current Dock Observer Service state:");
+ if (mUpdatesStopped) {
+ pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
+ }
+ pw.println(" reported state: " + mReportedDockState);
+ pw.println(" previous state: " + mPreviousDockState);
+ pw.println(" actual state: " + mActualDockState);
+ } else if (args.length == 3 && "set".equals(args[0])) {
+ String key = args[1];
+ String value = args[2];
+ try {
+ if ("state".equals(key)) {
+ mUpdatesStopped = true;
+ setDockStateLocked(Integer.parseInt(value));
+ } else {
+ pw.println("Unknown set option: " + key);
+ }
+ } catch (NumberFormatException ex) {
+ pw.println("Bad value: " + value);
+ }
+ } else if (args.length == 1 && "reset".equals(args[0])) {
+ mUpdatesStopped = false;
+ setDockStateLocked(mActualDockState);
+ } else {
+ pw.println("Dump current dock state, or:");
+ pw.println(" set state <value>");
+ pw.println(" reset");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fb69c86..251247c 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -961,6 +961,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
+
+ /**
+ * Returns true iff the caller is identified to be the current input method with the token.
+ * @param token The window token given to the input method when it was started.
+ * @return true if and only if non-null valid token is specified.
+ */
+ private boolean calledWithValidToken(IBinder token) {
+ if (token == null || mCurToken != token) {
+ return false;
+ }
+ return true;
+ }
+
private boolean bindCurrentInputMethodService(
Intent service, ServiceConnection conn, int flags) {
if (service == null || conn == null) {
@@ -1429,15 +1442,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void updateStatusIcon(IBinder token, String packageName, int iconId) {
- int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
- if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of uid " + uid + " token: " + token);
- return;
- }
-
synchronized (mMethodMap) {
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring updateStatusIcon due to an invalid token. uid:" + uid
+ + " token:" + token);
+ return;
+ }
if (iconId == 0) {
if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
if (mStatusBar != null) {
@@ -1527,9 +1540,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
final long ident = Binder.clearCallingIdentity();
try {
- if (token == null || mCurToken != token) {
- int uid = Binder.getCallingUid();
- Slog.w(TAG, "Ignoring setImeWindowStatus of uid " + uid + " token: " + token);
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring setImeWindowStatus due to an invalid token. uid:" + uid
+ + " token:" + token);
return;
}
synchronized (mMethodMap) {
@@ -1681,6 +1695,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
+ // Here is not the perfect place to reset the switching controller. Ideally
+ // mSwitchingController and mSettings should be able to share the same state.
+ // TODO: Make sure that mSwitchingController and mSettings are sharing the
+ // the same enabled IMEs list.
+ mSwitchingController.resetCircularListLocked(mContext);
}
/* package */ void setInputMethodLocked(String id, int subtypeId) {
@@ -2080,8 +2099,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
synchronized (mMethodMap) {
if (subtype != null) {
- setInputMethodWithSubtypeId(token, id, InputMethodUtils.getSubtypeIdFromHashCode(
- mMethodMap.get(id), subtype.hashCode()));
+ setInputMethodWithSubtypeIdLocked(token, id,
+ InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
+ subtype.hashCode()));
} else {
setInputMethod(token, id);
}
@@ -2168,7 +2188,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
+ ", from: " + mCurMethodId + ", " + subtypeId);
}
- setInputMethodWithSubtypeId(token, targetLastImiId, subtypeId);
+ setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
return true;
} else {
return false;
@@ -2182,12 +2202,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
synchronized (mMethodMap) {
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring switchToNextInputMethod due to an invalid token. uid:" + uid
+ + " token:" + token);
+ return false;
+ }
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
if (nextSubtype == null) {
return false;
}
- setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+ setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
+ nextSubtype.mSubtypeId);
return true;
}
}
@@ -2198,6 +2225,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
synchronized (mMethodMap) {
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring shouldOfferSwitchingToNextInputMethod due to an invalid "
+ + "token. uid:" + uid + " token:" + token);
+ return false;
+ }
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype);
if (nextSubtype == null) {
@@ -2277,14 +2310,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void notifyTextCommitted() {
+ public void notifyUserAction() {
if (DEBUG) {
- Slog.d(TAG, "Got the notification of commitText");
+ Slog.d(TAG, "Got the notification of a user action");
}
synchronized (mMethodMap) {
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
if (imi != null) {
- mSwitchingController.onCommitTextLocked(imi, mCurrentSubtype);
+ mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
}
}
}
@@ -2298,11 +2331,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
synchronized (mMethodMap) {
- if (token == null || mCurToken != token) {
- if (DEBUG) {
- Slog.w(TAG, "Ignoring setCursorAnchorMonitorMode from uid "
- + Binder.getCallingUid() + " token: " + token);
- }
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring setCursorAnchorMonitorMode due to an invalid token. uid:"
+ + uid + " token:" + token);
return;
}
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO(
@@ -2312,26 +2344,30 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
- if (token == null) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Using null token requires permission "
- + android.Manifest.permission.WRITE_SECURE_SETTINGS);
- }
- } else if (mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
- + " token: " + token);
- return;
- }
+ setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
+ }
+ }
- final long ident = Binder.clearCallingIdentity();
- try {
- setInputMethodLocked(id, subtypeId);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
+ if (token == null) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Using null token requires permission "
+ + android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
+ } else if (mCurToken != token) {
+ Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+ + " token: " + token);
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setInputMethodLocked(id, subtypeId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -2341,9 +2377,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
synchronized (mMethodMap) {
- if (token == null || mCurToken != token) {
- if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid "
- + Binder.getCallingUid() + " token: " + token);
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring hideInputMethod due to an invalid token. uid:"
+ + uid + " token:" + token);
return;
}
long ident = Binder.clearCallingIdentity();
@@ -2361,9 +2398,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
synchronized (mMethodMap) {
- if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring showMySoftInput of uid "
- + Binder.getCallingUid() + " token: " + token);
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring showMySoftInput due to an invalid token. uid:"
+ + uid + " token:" + token);
return;
}
long ident = Binder.clearCallingIdentity();
@@ -2650,7 +2688,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
setInputMethodEnabledLocked(defaultImiId, true);
}
}
-
+ // Here is not the perfect place to reset the switching controller. Ideally
+ // mSwitchingController and mSettings should be able to share the same state.
+ // TODO: Make sure that mSwitchingController and mSettings are sharing the
+ // the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 11fc941..98fa522 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -427,8 +427,9 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// bind to fused provider if supported
- if (FlpHardwareProvider.getInstance(mContext).isSupported()) {
- FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
+ if (FlpHardwareProvider.isSupported()) {
+ FlpHardwareProvider flpHardwareProvider =
+ FlpHardwareProvider.getInstance(mContext);
FusedProxy fusedProxy = FusedProxy.createAndBind(
mContext,
mLocationHandler,
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index cd8c13f..d8ee8a1 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -246,7 +246,8 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
private void setDeviceStateLocked(int headset,
int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
- int device;
+ int outDevice = 0;
+ int inDevice = 0;
int state;
if ((headsetState & headset) != 0) {
@@ -256,15 +257,16 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
}
if (headset == BIT_HEADSET) {
- device = AudioManager.DEVICE_OUT_WIRED_HEADSET;
+ outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
+ inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
} else if (headset == BIT_HEADSET_NO_MIC){
- device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
+ outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
} else if (headset == BIT_USB_HEADSET_ANLG) {
- device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
+ outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
} else if (headset == BIT_USB_HEADSET_DGTL) {
- device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
+ outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
} else if (headset == BIT_HDMI_AUDIO) {
- device = AudioManager.DEVICE_OUT_HDMI;
+ outDevice = AudioManager.DEVICE_OUT_HDMI;
} else {
Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
return;
@@ -273,7 +275,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
if (LOG)
Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected"));
- mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
+ if (outDevice != 0) {
+ mAudioManager.setWiredDeviceConnectionState(outDevice, state, headsetName);
+ }
+ if (inDevice != 0) {
+ mAudioManager.setWiredDeviceConnectionState(inDevice, state, headsetName);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 108a079..816e022 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -309,7 +309,7 @@ public final class ActiveServices {
}
ServiceRecord r = res.record;
NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
- callingUid, r.packageName, service, service.getFlags(), null);
+ callingUid, r.packageName, service, service.getFlags(), null, r.userId);
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
}
@@ -989,7 +989,8 @@ public final class ActiveServices {
sInfo.applicationInfo.packageName, sInfo.name);
if (userId > 0) {
if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
- sInfo.name, sInfo.flags)) {
+ sInfo.name, sInfo.flags)
+ && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {
userId = 0;
smap = getServiceMap(0);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fc808ec..fa26d56 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -283,7 +284,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
// could take much longer than usual.
- static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000;
+ static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
@@ -2189,7 +2190,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mBatteryStatsService.publish(mContext);
mUsageStatsService.publish(mContext);
mAppOpsService.publish(mContext);
-
+ Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, new LocalService());
}
@@ -2833,7 +2834,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return app;
}
- startProcessLocked(app, hostingType, hostingNameStr);
+ startProcessLocked(app, hostingType, hostingNameStr, null /* ABI override */);
return (app.pid != 0) ? app : null;
}
@@ -2842,7 +2843,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private final void startProcessLocked(ProcessRecord app,
- String hostingType, String hostingNameStr) {
+ String hostingType, String hostingNameStr, String abiOverride) {
if (app.pid > 0 && app.pid != MY_PID) {
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
@@ -2882,16 +2883,17 @@ public final class ActivityManagerService extends ActivityManagerNative
}
/*
- * Add shared application GID so applications can share some
- * resources like shared libraries
+ * Add shared application and profile GIDs so applications can share some
+ * resources like shared libraries and access user-wide resources
*/
if (permGids == null) {
- gids = new int[1];
+ gids = new int[2];
} else {
- gids = new int[permGids.length + 1];
- System.arraycopy(permGids, 0, gids, 1, permGids.length);
+ gids = new int[permGids.length + 2];
+ System.arraycopy(permGids, 0, gids, 2, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
+ gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
@@ -2927,7 +2929,7 @@ public final class ActivityManagerService extends ActivityManagerNative
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
- String requiredAbi = app.info.cpuAbi;
+ String requiredAbi = (abiOverride != null) ? abiOverride : app.info.cpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
}
@@ -2977,6 +2979,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
buf.append("}");
+ if (requiredAbi != null) {
+ buf.append(" abi=");
+ buf.append(requiredAbi);
+ }
Slog.i(TAG, buf.toString());
app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
@@ -5019,7 +5025,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
- addAppLocked(app.info, false);
+ addAppLocked(app.info, false, null /* ABI override */);
} else {
needRestart = true;
}
@@ -5132,7 +5138,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
- startProcessLocked(app, "link fail", processName);
+ startProcessLocked(app, "link fail", processName, null /* ABI override */);
return false;
}
@@ -5225,7 +5231,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
- startProcessLocked(app, "bind fail", processName);
+ startProcessLocked(app, "bind fail", processName, null /* ABI override */);
return false;
}
@@ -5376,7 +5382,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int ip=0; ip<NP; ip++) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
+ procs.get(ip));
- startProcessLocked(procs.get(ip), "on-hold", null);
+ startProcessLocked(procs.get(ip), "on-hold", null, null /* ABI override */);
}
}
@@ -6016,6 +6022,9 @@ public final class ActivityManagerService extends ActivityManagerNative
IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
+ if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
+ return false;
+ }
if (pi.applicationInfo.uid == uid) {
return true;
@@ -6377,7 +6386,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* Like checkGrantUriPermissionLocked, but takes an Intent.
*/
NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,
- String targetPkg, Intent intent, int mode, NeededUriGrants needed) {
+ String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Checking URI perm to data=" + (intent != null ? intent.getData() : null)
+ " clip=" + (intent != null ? intent.getClipData() : null)
@@ -6396,11 +6405,28 @@ public final class ActivityManagerService extends ActivityManagerNative
if (data == null && clip == null) {
return null;
}
-
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ int targetUid;
+ if (needed != null) {
+ targetUid = needed.targetUid;
+ } else {
+ try {
+ targetUid = pm.getPackageUid(targetPkg, targetUserId);
+ } catch (RemoteException ex) {
+ return null;
+ }
+ if (targetUid < 0) {
+ if (DEBUG_URI_PERMISSION) {
+ Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg
+ + " on user " + targetUserId);
+ }
+ return null;
+ }
+ }
if (data != null) {
GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), data);
- int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
- needed != null ? needed.targetUid : -1);
+ targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
+ targetUid);
if (targetUid > 0) {
if (needed == null) {
needed = new NeededUriGrants(targetPkg, targetUid, mode);
@@ -6412,10 +6438,9 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<clip.getItemCount(); i++) {
Uri uri = clip.getItemAt(i).getUri();
if (uri != null) {
- int targetUid = -1;
GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), uri);
targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
- needed != null ? needed.targetUid : -1);
+ targetUid);
if (targetUid > 0) {
if (needed == null) {
needed = new NeededUriGrants(targetPkg, targetUid, mode);
@@ -6426,7 +6451,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Intent clipIntent = clip.getItemAt(i).getIntent();
if (clipIntent != null) {
NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentLocked(
- callingUid, targetPkg, clipIntent, mode, needed);
+ callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
if (newNeeded != null) {
needed = newNeeded;
}
@@ -6453,9 +6478,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
void grantUriPermissionFromIntentLocked(int callingUid,
- String targetPkg, Intent intent, UriPermissionOwner owner) {
+ String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg,
- intent, intent != null ? intent.getFlags() : 0, null);
+ intent, intent != null ? intent.getFlags() : 0, null, targetUserId);
if (needed == null) {
return;
}
@@ -7734,7 +7759,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* in {@link ContentProvider}.
*/
private final String checkContentProviderPermissionLocked(
- ProviderInfo cpi, ProcessRecord r, int userId) {
+ ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) {
final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
@@ -7751,8 +7776,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
- userId = handleIncomingUser(callingPid, callingUid, userId,
- false, true, "checkContentProviderPermissionLocked", null);
+ if (checkUser) {
+ userId = handleIncomingUser(callingPid, callingUid, userId,
+ false, true, "checkContentProviderPermissionLocked " + cpi.authority, null);
+ }
if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
cpi.applicationInfo.uid, cpi.exported)
== PackageManager.PERMISSION_GRANTED) {
@@ -7887,13 +7914,34 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ boolean checkCrossUser = true;
+
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
+ // If that didn't work, check if it exists for user 0 and then
+ // verify that it's a singleton provider before using it.
+ if (cpr == null && userId != UserHandle.USER_OWNER) {
+ cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
+ if (cpr != null) {
+ cpi = cpr.info;
+ if (isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags)
+ && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
+ userId = UserHandle.USER_OWNER;
+ checkCrossUser = false;
+ } else {
+ cpr = null;
+ cpi = null;
+ }
+ }
+ }
+
boolean providerRunning = cpr != null;
if (providerRunning) {
cpi = cpr.info;
String msg;
- if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
+ if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
+ != null) {
throw new SecurityException(msg);
}
@@ -7973,15 +8021,21 @@ public final class ActivityManagerService extends ActivityManagerNative
if (cpi == null) {
return null;
}
+ // If the provider is a singleton AND
+ // (it's a call within the same user || the provider is a
+ // privileged app)
+ // Then allow connecting to the singleton provider
singleton = isSingleton(cpi.processName, cpi.applicationInfo,
- cpi.name, cpi.flags);
+ cpi.name, cpi.flags)
+ && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
if (singleton) {
- userId = 0;
+ userId = UserHandle.USER_OWNER;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
String msg;
- if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
+ if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
+ != null) {
throw new SecurityException(msg);
}
@@ -8517,7 +8571,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return new ProcessRecord(stats, info, proc, uid);
}
- final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
+ final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
+ String abiOverride) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(info.processName, info.uid, true);
@@ -8552,7 +8607,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
- startProcessLocked(app, "added application", app.processName);
+ startProcessLocked(app, "added application", app.processName,
+ abiOverride);
}
return app;
@@ -9769,7 +9825,7 @@ public final class ActivityManagerService extends ActivityManagerNative
= (ApplicationInfo)apps.get(i);
if (info != null &&
!info.packageName.equals("android")) {
- addAppLocked(info, false);
+ addAppLocked(info, false, null /* ABI override */);
}
}
}
@@ -10560,8 +10616,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// assume our apps are happy - lazy create the list
List<ActivityManager.ProcessErrorStateInfo> errList = null;
- final boolean allUsers = ActivityManager.checkUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
int userId = UserHandle.getUserId(Binder.getCallingUid());
@@ -10650,8 +10705,7 @@ public final class ActivityManagerService extends ActivityManagerNative
enforceNotIsolatedCaller("getRunningAppProcesses");
// Lazy instantiation of list
List<ActivityManager.RunningAppProcessInfo> runList = null;
- final boolean allUsers = ActivityManager.checkUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
int userId = UserHandle.getUserId(Binder.getCallingUid());
synchronized (this) {
@@ -12951,7 +13005,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// We have components that still need to be running in the
// process, so re-launch it.
mProcessNames.put(app.processName, app.uid, app);
- startProcessLocked(app, "restart", app.processName);
+ startProcessLocked(app, "restart", app.processName, null /* ABI override */);
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
boolean removed;
@@ -13091,8 +13145,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if ((requireFull || checkComponentPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS,
callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED)
- && checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ && checkComponentPermission(INTERACT_ACROSS_USERS_FULL,
callingPid, callingUid, -1, true)
!= PackageManager.PERMISSION_GRANTED) {
if (userId == UserHandle.USER_CURRENT_OR_SELF) {
@@ -13112,7 +13165,7 @@ public final class ActivityManagerService extends ActivityManagerNative
builder.append(" but is calling from user ");
builder.append(UserHandle.getUserId(callingUid));
builder.append("; this requires ");
- builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ builder.append(INTERACT_ACROSS_USERS_FULL);
if (!requireFull) {
builder.append(" or ");
builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
@@ -13142,8 +13195,9 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
String className, int flags) {
boolean result = false;
+ // For apps that don't have pre-defined UIDs, check for permission
if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
- if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
+ if ((flags & ServiceInfo.FLAG_SINGLE_USER) != 0) {
if (ActivityManager.checkUidPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS,
aInfo.uid) != PackageManager.PERMISSION_GRANTED) {
@@ -13154,12 +13208,14 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+ // Permission passed
result = true;
}
- } else if (componentProcessName == aInfo.packageName) {
- result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
} else if ("system".equals(componentProcessName)) {
result = true;
+ } else {
+ // App with pre-defined UID, check if it's a persistent app
+ result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
}
if (DEBUG_MU) {
Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo
@@ -13168,6 +13224,21 @@ public final class ActivityManagerService extends ActivityManagerNative
return result;
}
+ /**
+ * Checks to see if the caller is in the same app as the singleton
+ * component, or the component is in a special app. It allows special apps
+ * to export singleton components but prevents exporting singleton
+ * components for regular apps.
+ */
+ boolean isValidSingletonCall(int callingUid, int componentUid) {
+ int componentAppId = UserHandle.getAppId(componentUid);
+ return UserHandle.isSameApp(callingUid, componentUid)
+ || componentAppId == Process.SYSTEM_UID
+ || componentAppId == Process.PHONE_UID
+ || ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, componentUid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
@@ -13714,8 +13785,8 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
int callingAppId = UserHandle.getAppId(callingUid);
if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
- || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID ||
- callingUid == 0) {
+ || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
+ || callingUid == 0) {
// Always okay.
} else if (callerApp == null || !callerApp.persistent) {
try {
@@ -14248,7 +14319,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public boolean startInstrumentation(ComponentName className,
String profileFile, int flags, Bundle arguments,
IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
- int userId) {
+ int userId, String abiOverride) {
enforceNotIsolatedCaller("startInstrumentation");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, true, "startInstrumentation", null);
@@ -14297,7 +14368,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
"start instr");
- ProcessRecord app = addAppLocked(ai, false);
+ ProcessRecord app = addAppLocked(ai, false, abiOverride);
app.instrumentationClass = className;
app.instrumentationInfo = ai;
app.instrumentationProfileFile = profileFile;
@@ -16261,7 +16332,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.persistent) {
if (app.persistent) {
- addAppLocked(app.info, false);
+ addAppLocked(app.info, false, null /* ABI override */);
}
}
}
@@ -16525,12 +16596,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private boolean startUser(final int userId, boolean foreground) {
- if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: switchUser() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
@@ -16896,12 +16967,12 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public int stopUser(final int userId, final IStopUserCallback callback) {
- if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: switchUser() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
@@ -17039,7 +17110,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public UserInfo getCurrentUser() {
if ((checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) && (
- checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED)) {
String msg = "Permission Denial: getCurrentUser() from pid="
+ Binder.getCallingPid()
@@ -17125,12 +17196,12 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void registerUserSwitchObserver(IUserSwitchObserver observer) {
- if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: registerUserSwitchObserver() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b429b93..32722bc 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -635,7 +635,7 @@ final class ActivityRecord {
final void deliverNewIntentLocked(int callingUid, Intent intent) {
// The activity now gets access to the data associated with this Intent.
service.grantUriPermissionFromIntentLocked(callingUid, packageName,
- intent, getUriPermissionsLocked());
+ intent, getUriPermissionsLocked(), userId);
// We want to immediately deliver the intent to the activity if
// it is currently the top resumed activity... however, if the
// device is sleeping, then all activities are stopped, so in that
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1804d03..e6b465d 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2353,7 +2353,7 @@ final class ActivityStack {
if (callingUid > 0) {
mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- data, r.getUriPermissionsLocked());
+ data, r.getUriPermissionsLocked(), r.userId);
}
if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
@@ -2536,10 +2536,15 @@ final class ActivityStack {
if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
+ " who=" + r.resultWho + " req=" + r.requestCode
+ " res=" + resultCode + " data=" + resultData);
+ if (resultTo.userId != r.userId) {
+ if (resultData != null) {
+ resultData.prepareToLeaveUser(r.userId);
+ }
+ }
if (r.info.applicationInfo.uid > 0) {
mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
resultTo.packageName, resultData,
- resultTo.getUriPermissionsLocked());
+ resultTo.getUriPermissionsLocked(), resultTo.userId);
}
resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
resultData);
@@ -3796,6 +3801,7 @@ final class ActivityStack {
mStacks.remove(this);
mStacks.add(0, this);
}
+ mActivityContainer.onTaskListEmpty();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0cc53d1..35ac9c9 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -45,6 +45,7 @@ import android.app.PendingIntent;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.IActivityManager.WaitResult;
import android.app.ResultInfo;
+import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
@@ -73,6 +74,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
@@ -88,11 +90,13 @@ import android.view.Surface;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.TransferPipe;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -128,9 +132,16 @@ public final class ActivityStackSupervisor implements DisplayListener {
static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
static final int CONTAINER_CALLBACK_VISIBILITY = FIRST_SUPERVISOR_STACK_MSG + 8;
+ static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
+ static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
+ static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
+ /** Status Bar Service **/
+ private IBinder mToken = new Binder();
+ private IStatusBarService mStatusBarService;
+
// For debugging to make sure the caller when acquiring/releasing our
// wake lock is the system process.
static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
@@ -253,6 +264,21 @@ public final class ActivityStackSupervisor implements DisplayListener {
mLaunchingActivity.setReferenceCounted(false);
}
+ // This function returns a IStatusBarService. The value is from ServiceManager.
+ // getService and is cached.
+ private IStatusBarService getStatusBarService() {
+ synchronized (mService) {
+ if (mStatusBarService == null) {
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
+ if (mStatusBarService == null) {
+ Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
+ }
+ }
+ return mStatusBarService;
+ }
+ }
+
void setWindowManager(WindowManagerService wm) {
synchronized (mService) {
mWindowManager = wm;
@@ -1898,7 +1924,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r.getUriPermissionsLocked());
+ intent, r.getUriPermissionsLocked(), r.userId);
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
@@ -2942,9 +2968,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
void setLockTaskModeLocked(TaskRecord task) {
+ final Message lockTaskMsg = Message.obtain();
if (task == null) {
// Take out of lock task mode.
mLockTaskModeTask = null;
+ lockTaskMsg.what = LOCK_TASK_END_MSG;
+ mHandler.sendMessage(lockTaskMsg);
return;
}
if (isLockTaskModeViolation(task)) {
@@ -2954,6 +2983,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
mLockTaskModeTask = task;
findTaskToMoveToFrontLocked(task, 0, null);
resumeTopActivitiesLocked();
+ lockTaskMsg.what = LOCK_TASK_START_MSG;
+ mHandler.sendMessage(lockTaskMsg);
}
boolean isLockTaskModeViolation(TaskRecord task) {
@@ -3044,12 +3075,48 @@ public final class ActivityStackSupervisor implements DisplayListener {
} break;
case CONTAINER_CALLBACK_VISIBILITY: {
final ActivityContainer container = (ActivityContainer) msg.obj;
+ final IActivityContainerCallback callback = container.mCallback;
+ if (callback != null) {
+ try {
+ callback.setVisible(container.asBinder(), msg.arg1 == 1);
+ } catch (RemoteException e) {
+ }
+ }
+ } break;
+ case LOCK_TASK_START_MSG: {
+ // When lock task starts, we disable the status bars.
try {
- // We only send this message if mCallback is non-null.
- container.mCallback.setVisible(container.asBinder(), msg.arg1 == 1);
- } catch (RemoteException e) {
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable
+ (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK,
+ mToken, mService.mContext.getPackageName());
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
}
- }
+ } break;
+ case LOCK_TASK_END_MSG: {
+ // When lock task ends, we enable the status bars.
+ try {
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable
+ (StatusBarManager.DISABLE_NONE,
+ mToken, mService.mContext.getPackageName());
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ } break;
+ case CONTAINER_CALLBACK_TASK_LIST_EMPTY: {
+ final ActivityContainer container = (ActivityContainer) msg.obj;
+ final IActivityContainerCallback callback = container.mCallback;
+ if (callback != null) {
+ try {
+ callback.onAllActivitiesComplete(container.asBinder());
+ } catch (RemoteException e) {
+ }
+ }
+ } break;
}
}
}
@@ -3254,6 +3321,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
return true;
}
+ void onTaskListEmpty() {
+ mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget();
+ }
+
@Override
public String toString() {
return mIdString + (mActivityDisplay == null ? "N" : "A");
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 9d6481a..7b2b04d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -27,6 +27,7 @@ import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
@@ -858,7 +859,10 @@ public final class BroadcastQueue {
r.state = BroadcastRecord.APP_RECEIVE;
String targetProcess = info.activityInfo.processName;
r.curComponent = component;
- if (r.callingUid != Process.SYSTEM_UID && isSingleton) {
+ final int receiverUid = info.activityInfo.applicationInfo.uid;
+ // If it's a singleton, it needs to be the same app or a special app
+ if (r.callingUid != Process.SYSTEM_UID && isSingleton
+ && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
}
r.curReceiver = info.activityInfo;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 92b5f52..c93f85d 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -489,8 +489,10 @@ public class Tethering extends BaseNetworkObserver {
}
private class StateReceiver extends BroadcastReceiver {
+ @Override
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
+ if (action == null) { return; }
if (action.equals(UsbManager.ACTION_USB_STATE)) {
synchronized (Tethering.this.mPublicSync) {
boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index c502bf3..3b55bfc 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -230,7 +230,7 @@ public final class ContentService extends IContentService.Stub {
// Notify for any user other than the caller's own requires permission.
final int callingUserHandle = UserHandle.getCallingUserId();
if (userHandle != callingUserHandle) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
"no permission to notify other users");
}
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index 579f89f..e8862c9 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -124,7 +124,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
allocateDevices(ackedAddress);
startPhysicalAddressStage();
}
- }, DEVICE_POLLING_RETRY);
+ }, HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, DEVICE_POLLING_RETRY);
return true;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index d0b716d..5c420d7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -25,6 +25,7 @@ import android.os.MessageQueue;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.util.Predicate;
import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
import libcore.util.EmptyArray;
@@ -47,6 +48,21 @@ import java.util.List;
final class HdmiCecController {
private static final String TAG = "HdmiCecController";
+ /**
+ * Interface to report allocated logical address.
+ */
+ interface AllocateAddressCallback {
+ /**
+ * 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);
+ }
+
private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
// A message to pass cec send command to IO looper.
@@ -63,6 +79,22 @@ final class HdmiCecController {
private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
+ // Predicate for whether the given logical address is remote device's one or not.
+ private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
+ @Override
+ public boolean apply(Integer address) {
+ return !isAllocatedLocalDeviceAddress(address);
+ }
+ };
+
+ // Predicate whether the given logical address is system audio's one or not
+ private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
+ @Override
+ public boolean apply(Integer address) {
+ return HdmiCec.getTypeFromAddress(address) == HdmiCec.ADDR_AUDIO_SYSTEM;
+ }
+ };
+
// Handler instance to process synchronous I/O (mainly send) message.
private Handler mIoHandler;
@@ -116,45 +148,13 @@ final class HdmiCecController {
mNativePtr = nativePtr;
}
- /**
- * Perform initialization for each hosted device.
- *
- * @param deviceTypes array of device types
- */
- void initializeLocalDevices(int[] deviceTypes,
- HdmiCecLocalDevice.AddressAllocationCallback callback) {
- assertRunOnServiceThread();
- for (int type : deviceTypes) {
- HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type, callback);
- if (device == null) {
- continue;
- }
- // TODO: Consider restoring the local device addresses from persistent storage
- // to allocate the same addresses again if possible.
- device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
- mLocalDevices.put(type, device);
- device.init();
- }
- }
-
- /**
- * 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);
+ void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
+ mLocalDevices.put(deviceType, device);
}
/**
* Allocate a new logical address of the given device type. Allocated
- * address will be reported through {@link AllocateLogicalAddressCallback}.
+ * address will be reported through {@link AllocateAddressCallback}.
*
* <p> Declared as package-private, accessed by {@link HdmiControlService} only.
*
@@ -166,7 +166,7 @@ final class HdmiCecController {
* @param callback callback interface to report allocated logical address to caller
*/
void allocateLogicalAddress(final int deviceType, final int preferredAddress,
- final AllocateLogicalAddressCallback callback) {
+ final AllocateAddressCallback callback) {
assertRunOnServiceThread();
runOnIoThread(new Runnable() {
@@ -178,7 +178,7 @@ final class HdmiCecController {
}
private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
- final AllocateLogicalAddressCallback callback) {
+ final AllocateAddressCallback callback) {
assertRunOnIoThread();
int startAddress = preferredAddress;
// If preferred address is "unregistered", start address will be the smallest
@@ -275,10 +275,23 @@ final class HdmiCecController {
* Return a list of all {@link HdmiCecDeviceInfo}.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param includeLocalDevice whether to add local device or not
*/
- List<HdmiCecDeviceInfo> getDeviceInfoList() {
+ List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
assertRunOnServiceThread();
- return sparseArrayToList(mDeviceInfos);
+ if (includeLocalDevice) {
+ return sparseArrayToList(mDeviceInfos);
+ } else {
+ ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); ++i) {
+ HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
+ if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
+ infoList.add(info);
+ }
+ }
+ return infoList;
+ }
}
/**
@@ -380,18 +393,14 @@ final class HdmiCecController {
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*
* @param callback an interface used to get a list of all remote devices' address
+ * @param pickStrategy strategy how to pick polling candidates
* @param retryCount the number of retry used to send polling message to remote devices
*/
- void pollDevices(DevicePollingCallback callback, int retryCount) {
+ void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
assertRunOnServiceThread();
- // Extract polling candidates. No need to poll against local devices.
- ArrayList<Integer> pollingCandidates = new ArrayList<>();
- for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
- if (!isAllocatedLocalDeviceAddress(i)) {
- pollingCandidates.add(i);
- }
- }
+ // Extract polling candidates. No need to poll against local devices.
+ List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
runDevicePolling(pollingCandidates, retryCount, callback);
}
@@ -405,6 +414,41 @@ final class HdmiCecController {
return sparseArrayToList(mLocalDevices);
}
+ private List<Integer> pickPollCandidates(int pickStrategy) {
+ int strategy = pickStrategy & HdmiControlService.POLL_STRATEGY_MASK;
+ Predicate<Integer> pickPredicate = null;
+ switch (strategy) {
+ case HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO:
+ pickPredicate = mSystemAudioAddressPredicate;
+ break;
+ case HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES:
+ default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
+ pickPredicate = mRemoteDeviceAddressPredicate;
+ break;
+ }
+
+ int iterationStrategy = pickStrategy & HdmiControlService.POLL_ITERATION_STRATEGY_MASK;
+ ArrayList<Integer> pollingCandidates = new ArrayList<>();
+ switch (iterationStrategy) {
+ case HdmiControlService.POLL_ITERATION_IN_ORDER:
+ for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
+ if (pickPredicate.apply(i)) {
+ pollingCandidates.add(i);
+ }
+ }
+ break;
+ case HdmiControlService.POLL_ITERATION_REVERSE_ORDER:
+ default: // The default is reverse order.
+ for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
+ if (pickPredicate.apply(i)) {
+ pollingCandidates.add(i);
+ }
+ }
+ break;
+ }
+ return pollingCandidates;
+ }
+
private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
ArrayList<T> list = new ArrayList<>();
for (int i = 0; i < array.size(); ++i) {
@@ -505,7 +549,7 @@ final class HdmiCecController {
// Reply <Feature Abort> to initiator (source) for all requests.
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
sourceAddress, message.getSource(), message.getOpcode(),
- HdmiCecMessageBuilder.ABORT_REFUSED);
+ HdmiConstants.ABORT_REFUSED);
sendCommand(cecMessage, null);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index aac2a15..23454ad 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -16,8 +16,6 @@
package com.android.server.hdmi;
-import com.android.server.hdmi.HdmiCecController.AllocateLogicalAddressCallback;
-
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
@@ -27,84 +25,43 @@ import android.hardware.hdmi.HdmiCecDeviceInfo;
*/
abstract class HdmiCecLocalDevice {
- protected final HdmiCecController mController;
+ protected final HdmiControlService mService;
protected final int mDeviceType;
- protected final AddressAllocationCallback mAllocationCallback;
protected int mAddress;
protected int mPreferredAddress;
protected HdmiCecDeviceInfo mDeviceInfo;
- /**
- * Callback interface to notify newly allocated logical address of the given
- * local device.
- */
- interface AddressAllocationCallback {
- /**
- * Called when a logical address of the given device is allocated.
- *
- * @param deviceType original device type
- * @param logicalAddress newly allocated logical address
- */
- void onAddressAllocated(int deviceType, int logicalAddress);
- }
-
- protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType,
- AddressAllocationCallback callback) {
- mController = controller;
+ protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
+ mService = service;
mDeviceType = deviceType;
- mAllocationCallback = callback;
mAddress = HdmiCec.ADDR_UNREGISTERED;
}
// Factory method that returns HdmiCecLocalDevice of corresponding type.
- static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType,
- AddressAllocationCallback callback) {
+ static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
switch (deviceType) {
case HdmiCec.DEVICE_TV:
- return new HdmiCecLocalDeviceTv(controller, callback);
+ return new HdmiCecLocalDeviceTv(service);
case HdmiCec.DEVICE_PLAYBACK:
- return new HdmiCecLocalDevicePlayback(controller, callback);
+ return new HdmiCecLocalDevicePlayback(service);
default:
return null;
}
}
- abstract void init();
+ void init() {
+ mPreferredAddress = HdmiCec.ADDR_UNREGISTERED;
+ // TODO: load preferred address from permanent storage.
+ }
/**
- * Called when a logical address of the local device is allocated.
- * Note that internal variables are updated before it's called.
+ * Called once a logical address of the local device is allocated.
*/
protected abstract void onAddressAllocated(int logicalAddress);
- protected void allocateAddress(int type) {
- mController.allocateLogicalAddress(type, mPreferredAddress,
- new AllocateLogicalAddressCallback() {
- @Override
- public void onAllocated(int deviceType, int logicalAddress) {
- mAddress = mPreferredAddress = logicalAddress;
-
- // Create and set device info.
- HdmiCecDeviceInfo deviceInfo = createDeviceInfo(mAddress, deviceType);
- setDeviceInfo(deviceInfo);
- mController.addDeviceInfo(deviceInfo);
-
- mController.addLogicalAddress(logicalAddress);
- onAddressAllocated(logicalAddress);
- if (mAllocationCallback != null) {
- mAllocationCallback.onAddressAllocated(deviceType, logicalAddress);
- }
- }
- });
- }
-
- private final HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
- int vendorId = mController.getVendorId();
- int physicalAddress = mController.getPhysicalAddress();
- // TODO: get device name read from system configuration.
- String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
- return new HdmiCecDeviceInfo(logicalAddress,
- physicalAddress, deviceType, vendorId, displayName);
+ final void handleAddressAllocated(int logicalAddress) {
+ mAddress = mPreferredAddress = logicalAddress;
+ onAddressAllocated(logicalAddress);
}
HdmiCecDeviceInfo getDeviceInfo() {
@@ -128,4 +85,8 @@ abstract class HdmiCecLocalDevice {
void setPreferredAddress(int addr) {
mPreferredAddress = addr;
}
+
+ int getPreferredAddress() {
+ return mPreferredAddress;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 3347725..d79e283 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -23,18 +23,13 @@ import android.hardware.hdmi.HdmiCec;
*/
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
- HdmiCecLocalDevicePlayback(HdmiCecController controller, AddressAllocationCallback callback) {
- super(controller, HdmiCec.DEVICE_PLAYBACK, callback);
- }
-
- @Override
- void init() {
- allocateAddress(mDeviceType);
+ HdmiCecLocalDevicePlayback(HdmiControlService service) {
+ super(service, HdmiCec.DEVICE_PLAYBACK);
}
@Override
protected void onAddressAllocated(int logicalAddress) {
- mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, mController.getPhysicalAddress(), mDeviceType));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mService.getPhysicalAddress(), mDeviceType));
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 93761ab..72d7f2d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -23,24 +23,20 @@ import android.hardware.hdmi.HdmiCec;
*/
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
- HdmiCecLocalDeviceTv(HdmiCecController controller, AddressAllocationCallback callback) {
- super(controller, HdmiCec.DEVICE_TV, callback);
- }
-
- @Override
- void init() {
- allocateAddress(mDeviceType);
+ HdmiCecLocalDeviceTv(HdmiControlService service) {
+ super(service, HdmiCec.DEVICE_TV);
}
@Override
protected void onAddressAllocated(int logicalAddress) {
// TODO: vendor-specific initialization here.
- mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, mController.getPhysicalAddress(), mDeviceType));
- mController.sendCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
- mAddress, mController.getVendorId()));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mService.getPhysicalAddress(), mDeviceType));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mAddress, mService.getVendorId()));
+ mService.launchDeviceDiscovery(mAddress);
// TODO: Start routing control action, device discovery action.
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 9a76734..6c2be34 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -26,15 +26,6 @@ import java.util.Arrays;
* A helper class to build {@link HdmiCecMessage} from various cec commands.
*/
public class HdmiCecMessageBuilder {
- // TODO: move these values to HdmiCec.java once make it internal constant class.
- // CEC's ABORT reason values.
- static final int ABORT_UNRECOGNIZED_MODE = 0;
- static final int ABORT_NOT_IN_CORRECT_MODE = 1;
- static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
- static final int ABORT_INVALID_OPERAND = 3;
- static final int ABORT_REFUSED = 4;
- static final int ABORT_UNABLE_TO_DETERMINE = 5;
-
private static final int OSD_NAME_MAX_LENGTH = 13;
private HdmiCecMessageBuilder() {}
@@ -290,6 +281,64 @@ public class HdmiCecMessageBuilder {
}
/**
+ * Build &lt;System Audio Mode Request&gt; command.
+ *
+ * @param src source address of command
+ * @param avr destination address of command, it should be AVR
+ * @param avrPhysicalAddress physical address of AVR
+ * @param enableSystemAudio whether to enable System Audio Mode or not
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
+ boolean enableSystemAudio) {
+ if (enableSystemAudio) {
+ return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
+ physicalAddressToParam(avrPhysicalAddress));
+ } else {
+ return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
+ }
+ }
+
+ /**
+ * Build &lt;Give Audio Status&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
+ return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_AUDIO_STATUS);
+ }
+
+ /**
+ * Build &lt;User Control Pressed&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param uiCommand keycode that user pressed
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
+ byte[] params = new byte[] {
+ (byte) uiCommand
+ };
+ return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params);
+ }
+
+ /**
+ * Build &lt;User Control Released&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildUserControlReleased(int src, int dest) {
+ return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
+ }
+
+ /***** Please ADD new buildXXX() methods above. ******/
+
+ /**
* Build a {@link HdmiCecMessage} without extra parameter.
*
* @param src source address of command
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
new file mode 100644
index 0000000..a83d1ed
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+/**
+ * Defines constants related to HDMI-CEC protocol internal implementation.
+ * If a constant will be used in the public api, it should be located in
+ * {@link android.hardware.hdmi.HdmiCec}.
+ */
+final class HdmiConstants {
+
+ // Constants related to operands of HDMI CEC commands.
+ // Refer to CEC Table 29 in HDMI Spec v1.4b.
+ // [Abort Reason]
+ static final int ABORT_UNRECOGNIZED_MODE = 0;
+ static final int ABORT_NOT_IN_CORRECT_MODE = 1;
+ static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
+ static final int ABORT_INVALID_OPERAND = 3;
+ static final int ABORT_REFUSED = 4;
+ static final int ABORT_UNABLE_TO_DETERMINE = 5;
+
+ // [Audio Status]
+ static final int SYSTEM_AUDIO_STATUS_OFF = 0;
+ static final int SYSTEM_AUDIO_STATUS_ON = 1;
+
+ // Constants related to UI Command Codes.
+ // Refer to CEC Table 30 in HDMI Spec v1.4b.
+ static final int UI_COMMAND_MUTE = 0x43;
+ static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
+ static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;
+
+ private HdmiConstants() { /* cannot be instantiated */ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index d775733..4b78591 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -30,12 +30,13 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
-import com.android.server.hdmi.HdmiCecLocalDevice.AddressAllocationCallback;
+import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import java.util.ArrayList;
import java.util.Iterator;
@@ -57,6 +58,14 @@ public final class HdmiControlService extends SystemService {
static final int SEND_RESULT_NAK = -1;
static final int SEND_RESULT_FAILURE = -2;
+ static final int POLL_STRATEGY_MASK = 0x3; // first and second bit.
+ static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
+ static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
+
+ static final int POLL_ITERATION_STRATEGY_MASK = 0x30000; // first and second bit.
+ static final int POLL_ITERATION_IN_ORDER = 0x10000;
+ static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
+
/**
* Interface to report send result.
*/
@@ -114,12 +123,17 @@ public final class HdmiControlService extends SystemService {
@Nullable
private HdmiMhlController mMhlController;
+ @GuardedBy("mLock")
// Whether ARC is "enabled" or not.
// TODO: it may need to hold lock if it's accessed from others.
private boolean mArcStatusEnabled = false;
+ @GuardedBy("mLock")
+ // Whether SystemAudioMode is "On" or not.
+ private boolean mSystemAudioMode;
+
// Handler running on service thread. It's used to run a task in service thread.
- private Handler mHandler = new Handler();
+ private final Handler mHandler = new Handler();
public HdmiControlService(Context context) {
super(context);
@@ -131,23 +145,9 @@ public final class HdmiControlService extends SystemService {
public void onStart() {
mIoThread.start();
mCecController = HdmiCecController.create(this);
- if (mCecController != null) {
- mCecController.initializeLocalDevices(mLocalDevices, new AddressAllocationCallback() {
- private final SparseIntArray mAllocated = new SparseIntArray();
- @Override
- public void onAddressAllocated(int deviceType, int logicalAddress) {
- mAllocated.append(deviceType, logicalAddress);
- // TODO: get HdmiLCecLocalDevice and call onAddressAllocated here.
-
- // Once all logical allocation is done, launch device discovery
- // action if one of local device is TV.
- int tvAddress = mAllocated.get(HdmiCec.DEVICE_TV, -1);
- if (mLocalDevices.length == mAllocated.size() && tvAddress != -1) {
- launchDeviceDiscovery(tvAddress);
- }
- }
- });
+ if (mCecController != null) {
+ initializeLocalDevices(mLocalDevices);
} else {
Slog.i(TAG, "Device does not support HDMI-CEC.");
}
@@ -158,6 +158,49 @@ public final class HdmiControlService extends SystemService {
}
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
+
+ // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
+ // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
+ }
+
+ private void initializeLocalDevices(final int[] deviceTypes) {
+ // A container for [Logical Address, Local device info].
+ final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
+ final SparseIntArray finished = new SparseIntArray();
+ for (int type : deviceTypes) {
+ final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
+ localDevice.init();
+ mCecController.allocateLogicalAddress(type,
+ localDevice.getPreferredAddress(), new AllocateAddressCallback() {
+ @Override
+ public void onAllocated(int deviceType, int logicalAddress) {
+ if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
+ Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
+ } else {
+ HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
+ localDevice.setDeviceInfo(deviceInfo);
+ mCecController.addLocalDevice(deviceType, localDevice);
+ mCecController.addLogicalAddress(logicalAddress);
+ devices.append(logicalAddress, localDevice);
+ }
+ finished.append(deviceType, logicalAddress);
+
+ // Once finish address allocation for all devices, notify
+ // it to each device.
+ if (deviceTypes.length == finished.size()) {
+ notifyAddressAllocated(devices);
+ }
+ }
+ });
+ }
+ }
+
+ private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
+ for (int i = 0; i < devices.size(); ++i) {
+ int address = devices.keyAt(i);
+ HdmiCecLocalDevice device = devices.valueAt(i);
+ device.onAddressAllocated(address);
+ }
}
/**
@@ -180,6 +223,30 @@ public final class HdmiControlService extends SystemService {
}
/**
+ * Returns physical address of the device.
+ */
+ int getPhysicalAddress() {
+ return mCecController.getPhysicalAddress();
+ }
+
+ /**
+ * Returns vendor id of CEC service.
+ */
+ int getVendorId() {
+ return mCecController.getVendorId();
+ }
+
+ /**
+ * Returns a list of {@link HdmiCecDeviceInfo}.
+ *
+ * @param includeLocalDevice whether to include local devices
+ */
+ List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
+ assertRunOnServiceThread();
+ return mCecController.getDeviceInfoList(includeLocalDevice);
+ }
+
+ /**
* Add and start a new {@link FeatureAction} to the action queue.
*
* @param action {@link FeatureAction} to add and start
@@ -195,6 +262,18 @@ public final class HdmiControlService extends SystemService {
});
}
+ void setSystemAudioMode(boolean on) {
+ synchronized (mLock) {
+ mSystemAudioMode = on;
+ }
+ }
+
+ boolean getSystemAudioMode() {
+ synchronized (mLock) {
+ return mSystemAudioMode;
+ }
+ }
+
// See if we have an action of a given type in progress.
private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
for (FeatureAction action : mActions) {
@@ -211,48 +290,58 @@ public final class HdmiControlService extends SystemService {
* @param action {@link FeatureAction} to remove
*/
void removeAction(final FeatureAction action) {
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- mActions.remove(action);
- }
- });
+ assertRunOnServiceThread();
+ mActions.remove(action);
}
// Remove all actions matched with the given Class type.
private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- Iterator<FeatureAction> iter = mActions.iterator();
- while (iter.hasNext()) {
- FeatureAction action = iter.next();
- if (action.getClass().equals(clazz)) {
- action.clear();
- mActions.remove(action);
- }
- }
+ removeActionExcept(clazz, null);
+ }
+
+ // Remove all actions matched with the given Class type besides |exception|.
+ <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ assertRunOnServiceThread();
+ Iterator<FeatureAction> iter = mActions.iterator();
+ while (iter.hasNext()) {
+ FeatureAction action = iter.next();
+ if (action != exception && action.getClass().equals(clazz)) {
+ action.clear();
+ mActions.remove(action);
}
- });
+ }
}
private void runOnServiceThread(Runnable runnable) {
mHandler.post(runnable);
}
+ void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
+ mHandler.postAtFrontOfQueue(runnable);
+ }
+
+ private void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mHandler.getLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
/**
* Change ARC status into the given {@code enabled} status.
*
* @return {@code true} if ARC was in "Enabled" status
*/
boolean setArcStatus(boolean enabled) {
- boolean oldStatus = mArcStatusEnabled;
- // 1. Enable/disable ARC circuit.
- // TODO: call set_audio_return_channel of hal interface.
+ synchronized (mLock) {
+ boolean oldStatus = mArcStatusEnabled;
+ // 1. Enable/disable ARC circuit.
+ // TODO: call set_audio_return_channel of hal interface.
- // 2. Update arc status;
- mArcStatusEnabled = enabled;
- return oldStatus;
+ // 2. Update arc status;
+ mArcStatusEnabled = enabled;
+ return oldStatus;
+ }
}
/**
@@ -306,8 +395,12 @@ public final class HdmiControlService extends SystemService {
case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
handleReportPhysicalAddress(message);
return true;
- // TODO: Add remaining system information query such as
- // <Give Device Power Status> and <Request Active Source> handler.
+ case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ handleSetSystemAudioMode(message);
+ return true;
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ handleSystemAudioModeStatus(message);
+ return true;
default:
return dispatchMessageToAction(message);
}
@@ -328,10 +421,63 @@ public final class HdmiControlService extends SystemService {
* devices.
*
* @param callback an interface used to get a list of all remote devices' address
+ * @param pickStrategy strategy how to pick polling candidates
* @param retryCount the number of retry used to send polling message to remote devices
+ * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
+ */
+ void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
+ mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount);
+ }
+
+ private int checkPollStrategy(int pickStrategy) {
+ int strategy = pickStrategy & POLL_STRATEGY_MASK;
+ if (strategy == 0) {
+ throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
+ }
+ int iterationStrategy = pickStrategy & POLL_ITERATION_STRATEGY_MASK;
+ if (iterationStrategy == 0) {
+ throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
+ }
+ return strategy | iterationStrategy;
+ }
+
+
+ /**
+ * Launch device discovery sequence. It starts with clearing the existing device info list.
+ * Note that it assumes that logical address of all local devices is already allocated.
+ *
+ * @param sourceAddress a logical address of tv
*/
- void pollDevices(DevicePollingCallback callback, int retryCount) {
- mCecController.pollDevices(callback, retryCount);
+ void launchDeviceDiscovery(final int sourceAddress) {
+ // At first, clear all existing device infos.
+ mCecController.clearDeviceInfoList();
+
+ // TODO: check whether TV is one of local devices.
+ DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
+ new DeviceDiscoveryCallback() {
+ @Override
+ public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
+ for (HdmiCecDeviceInfo info : deviceInfos) {
+ mCecController.addDeviceInfo(info);
+ }
+
+ // Add device info of all local devices.
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ mCecController.addDeviceInfo(device.getDeviceInfo());
+ }
+
+ addAndStartAction(new HotplugDetectionAction(HdmiControlService.this,
+ sourceAddress));
+ }
+ });
+ addAndStartAction(action);
+ }
+
+ private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
+ // TODO: get device name read from system configuration.
+ String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
+ return new HdmiCecDeviceInfo(logicalAddress,
+ getPhysicalAddress(), deviceType, getVendorId(), displayName);
}
private void handleReportPhysicalAddress(HdmiCecMessage message) {
@@ -340,7 +486,7 @@ public final class HdmiControlService extends SystemService {
return;
}
- // Ignore if [Device Discovery Action] is on going ignore message.
+ // Ignore if [Device Discovery Action] is going on.
if (hasAction(DeviceDiscoveryAction.class)) {
Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
+ "because Device Discovery Action is on-going:" + message);
@@ -413,7 +559,7 @@ public final class HdmiControlService extends SystemService {
sendCecCommand(
HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
- HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
+ HdmiConstants.ABORT_UNRECOGNIZED_MODE));
return;
}
@@ -438,6 +584,33 @@ public final class HdmiControlService extends SystemService {
return false;
}
+ private void handleSetSystemAudioMode(HdmiCecMessage message) {
+ if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
+ return;
+ }
+ SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
+ message.getDestination(), message.getSource(),
+ HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ addAndStartAction(action);
+ }
+
+ private void handleSystemAudioModeStatus(HdmiCecMessage message) {
+ if (!isMessageForSystemAudio(message)) {
+ return;
+ }
+ setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ }
+
+ private boolean isMessageForSystemAudio(HdmiCecMessage message) {
+ if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
+ || message.getDestination() != HdmiCec.ADDR_TV
+ || getAvrDeviceInfo() == null) {
+ Slog.w(TAG, "Skip abnormal CecMessage: " + message);
+ return false;
+ }
+ return true;
+ }
+
// Record class that monitors the event of the caller of being killed. Used to clean up
// the listener list and record list accordingly.
private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -460,34 +633,6 @@ public final class HdmiControlService extends SystemService {
mCecController.addDeviceInfo(info);
}
- // Launch device discovery sequence.
- // It starts with clearing the existing device info list.
- // Note that it assumes that logical address of all local devices is already allocated.
- private void launchDeviceDiscovery(int sourceAddress) {
- // At first, clear all existing device infos.
- mCecController.clearDeviceInfoList();
-
- // TODO: check whether TV is one of local devices.
- DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
- new DeviceDiscoveryCallback() {
- @Override
- public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
- for (HdmiCecDeviceInfo info : deviceInfos) {
- mCecController.addDeviceInfo(info);
- }
-
- // Add device info of all local devices.
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
- mCecController.addDeviceInfo(device.getDeviceInfo());
- }
-
- // TODO: start hot-plug detection sequence here.
- // addAndStartAction(new HotplugDetectionAction());
- }
- });
- addAndStartAction(action);
- }
-
private void enforceAccessPermission() {
getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
}
@@ -627,4 +772,17 @@ public final class HdmiControlService extends SystemService {
Slog.e(TAG, "Invoking callback failed:" + e);
}
}
+
+ HdmiCecDeviceInfo getAvrDeviceInfo() {
+ return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ // TODO: Hook up with AudioManager.
+ }
+
+ boolean isInPresetInstallationMode() {
+ // TODO: Implement this.
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
new file mode 100644
index 0000000..ef128ed1
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.util.Slog;
+
+/**
+ * Various utilities to handle HDMI CEC messages.
+ */
+final class HdmiUtils {
+
+ private HdmiUtils() { /* cannot be instantiated */ }
+
+ /**
+ * Verify if the given address is for the given device type. If not it will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param logicalAddress the logical address to verify
+ * @param deviceType the device type to check
+ * @throw IllegalArgumentException
+ */
+ static void verifyAddressType(int logicalAddress, int deviceType) {
+ int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
+ if (actualDeviceType != deviceType) {
+ throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
+ + ", Actual:" + actualDeviceType);
+ }
+ }
+
+ /**
+ * Check if the given CEC message come from the given address.
+ *
+ * @param cmd the CEC message to check
+ * @param expectedAddress the expected source address of the given message
+ * @param tag the tag of caller module (for log message)
+ * @return true if the CEC message comes from the given address
+ */
+ static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) {
+ int src = cmd.getSource();
+ if (src != expectedAddress) {
+ Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Parse the parameter block of CEC message as [System Audio Status].
+ *
+ * @param cmd the CEC message to parse
+ * @return true if the given parameter has [ON] value
+ */
+ static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
+ // TODO: Handle the exception when the length is wrong.
+ return cmd.getParams().length > 0
+ && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
new file mode 100644
index 0000000..c905c76
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -0,0 +1,193 @@
+/*
+ * 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 com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Feature action that handles hot-plug detection mechanism.
+ * Hot-plug event is initiated by timer after device discovery action.
+ *
+ * <p>Check all devices every 15 secs except for system audio.
+ * If system audio is on, check hot-plug for audio system every 5 secs.
+ * For other devices, keep 15 secs period.
+ */
+final class HotplugDetectionAction extends FeatureAction {
+ private static final String TAG = "HotPlugDetectionAction";
+
+ private static final int POLLING_INTERVAL_MS = 5000;
+ private static final int TIMEOUT_COUNT = 3;
+ private static final int POLL_RETRY_COUNT = 2;
+
+ // State in which waits for next polling
+ private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
+
+ // All addresses except for broadcast (unregistered address).
+ private static final int NUM_OF_ADDRESS = HdmiCec.ADDR_SPECIFIC_USE - HdmiCec.ADDR_TV + 1;
+
+ private int mTimeoutCount = 0;
+
+ /**
+ * Constructor
+ *
+ * @param service instance of {@link HdmiControlService}
+ * @param sourceAddress logical address of a device that initiate this action
+ */
+ HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
+ super(service, sourceAddress);
+ }
+
+ @Override
+ boolean start() {
+ Slog.v(TAG, "Hot-plug dection started.");
+
+ mState = STATE_WAIT_FOR_NEXT_POLLING;
+ mTimeoutCount = 0;
+
+ // Start timer without polling.
+ // The first check for all devices will be initiated 15 seconds later.
+ addTimer(mState, POLLING_INTERVAL_MS);
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ // No-op
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ if (mState == STATE_WAIT_FOR_NEXT_POLLING) {
+ mTimeoutCount = (mTimeoutCount + 1) % TIMEOUT_COUNT;
+ pollDevices();
+ }
+ }
+
+ // This method is called every 5 seconds.
+ private void pollDevices() {
+ // All device check called every 15 seconds.
+ if (mTimeoutCount == 0) {
+ pollAllDevices();
+ } else {
+ if (mService.getSystemAudioMode()) {
+ pollAudioSystem();
+ }
+ }
+
+ addTimer(mState, POLLING_INTERVAL_MS);
+ }
+
+ private void pollAllDevices() {
+ Slog.v(TAG, "Poll all devices.");
+
+ mService.pollDevices(new DevicePollingCallback() {
+ @Override
+ public void onPollingFinished(List<Integer> ackedAddress) {
+ checkHotplug(ackedAddress, false);
+ }
+ }, HdmiControlService.POLL_ITERATION_IN_ORDER
+ | HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, POLL_RETRY_COUNT);
+ }
+
+ private void pollAudioSystem() {
+ Slog.v(TAG, "Poll audio system.");
+
+ mService.pollDevices(new DevicePollingCallback() {
+ @Override
+ public void onPollingFinished(List<Integer> ackedAddress) {
+ checkHotplug(ackedAddress, false);
+ }
+ }, HdmiControlService.POLL_ITERATION_IN_ORDER
+ | HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT);
+ }
+
+ private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
+ BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
+ BitSet polledResult = addressListToBitSet(ackedAddress);
+
+ // At first, check removed devices.
+ BitSet removed = complement(currentInfos, polledResult);
+ int index = -1;
+ while ((index = removed.nextSetBit(index + 1)) != -1) {
+ Slog.v(TAG, "Remove device by hot-plug detection:" + index);
+ removeDevice(index);
+ }
+
+ // Next, check added devices.
+ BitSet added = complement(polledResult, currentInfos);
+ index = -1;
+ while ((index = added.nextSetBit(index + 1)) != -1) {
+ Slog.v(TAG, "Add device by hot-plug detection:" + index);
+ addDevice(index);
+ }
+ }
+
+ private static BitSet infoListToBitSet(List<HdmiCecDeviceInfo> infoList, boolean audioOnly) {
+ BitSet set = new BitSet(NUM_OF_ADDRESS);
+ for (HdmiCecDeviceInfo info : infoList) {
+ if (audioOnly) {
+ if (info.getDeviceType() == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+ set.set(info.getLogicalAddress());
+ }
+ } else {
+ set.set(info.getLogicalAddress());
+ }
+ }
+ return set;
+ }
+
+ private static BitSet addressListToBitSet(List<Integer> list) {
+ BitSet set = new BitSet(NUM_OF_ADDRESS);
+ for (Integer value : list) {
+ set.set(value);
+ }
+ return set;
+ }
+
+ // A - B = A & ~B
+ private static BitSet complement(BitSet first, BitSet second) {
+ // Need to clone it so that it doesn't touch original set.
+ BitSet clone = (BitSet) first.clone();
+ clone.andNot(second);
+ return clone;
+ }
+
+ private void addDevice(int addedAddress) {
+ // TODO: implement this.
+ }
+
+ private void removeDevice(int removedAddress) {
+ // TODO: implements following steps.
+ // 1. Launch routing control sequence
+ // 2. Stop one touch play sequence if removed device is the device to be selected.
+ // 3. If audio system, start system audio off and arc off
+ // 4. Inform device remove to others
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 05614a4..08ca306 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -44,28 +44,15 @@ abstract class RequestArcAction extends FeatureAction {
*/
RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) {
super(service, sourceAddress);
- verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
- verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+ HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
}
- private static void verifyAddressType(int logicalAddress, int deviceType) {
- int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
- if (actualDeviceType != deviceType) {
- throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
- + ", Actual:" + actualDeviceType);
- }
- }
-
@Override
boolean processCommand(HdmiCecMessage cmd) {
- if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
- return false;
- }
-
- int src = cmd.getSource();
- if (src != mAvrAddress) {
- Slog.w(TAG, "Invalid source [Expected:" + mAvrAddress + ", Actual:" + src + "]");
+ if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE
+ || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) {
return false;
}
int opcode = cmd.getOpcode();
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index e3525d8..d53d88d 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -46,21 +46,12 @@ final class SetArcTransmissionStateAction extends FeatureAction {
SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
boolean enabled) {
super(service, sourceAddress);
- verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
- verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+ HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
mEnabled = enabled;
}
- // TODO: extract it as separate utility class.
- private static void verifyAddressType(int logicalAddress, int deviceType) {
- int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
- if (actualDeviceType != deviceType) {
- throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
- + ", Actual:" + actualDeviceType);
- }
- }
-
@Override
boolean start() {
if (mEnabled) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
new file mode 100644
index 0000000..dde3342
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -0,0 +1,192 @@
+/*
+ * 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;
+
+/**
+ * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr.
+ */
+abstract class SystemAudioAction extends FeatureAction {
+ private static final String TAG = "SystemAudioAction";
+
+ // State in which waits for <SetSystemAudioMode>.
+ private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 1;
+
+ // State in which waits for <ReportAudioStatus>.
+ private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 2;
+
+ private static final int MAX_SEND_RETRY_COUNT = 2;
+
+ private static final int ON_TIMEOUT_MS = 5000;
+ private static final int OFF_TIMEOUT_MS = TIMEOUT_MS;
+
+ // Logical address of AV Receiver.
+ protected final int mAvrLogicalAddress;
+
+ // The target audio status of the action, whether to enable the system audio mode or not.
+ protected boolean mTargetAudioStatus;
+
+ private int mSendRetryCount = 0;
+
+ /**
+ * Constructor
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param sourceAddress logical address of source device (TV or STB).
+ * @param avrAddress logical address of AVR device
+ * @param targetStatus Whether to enable the system audio mode or not
+ * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
+ */
+ SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress,
+ boolean targetStatus) {
+ super(service, sourceAddress);
+ HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+ mAvrLogicalAddress = avrAddress;
+ mTargetAudioStatus = targetStatus;
+ }
+
+ protected void sendSystemAudioModeRequest() {
+ int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress();
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress,
+ mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
+ addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
+ } else {
+ setSystemAudioMode(false);
+ finish();
+ }
+ }
+ });
+ }
+
+ private void handleSendSystemAudioModeRequestTimeout() {
+ if (!mTargetAudioStatus // Don't retry for Off case.
+ || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
+ setSystemAudioMode(false);
+ finish();
+ return;
+ }
+ sendSystemAudioModeRequest();
+ }
+
+ protected void setSystemAudioMode(boolean mode) {
+ mService.setSystemAudioMode(mode);
+ }
+
+ protected void sendGiveAudioStatus() {
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress,
+ mAvrLogicalAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ handleSendGiveAudioStatusFailure();
+ }
+ }
+ });
+ }
+
+ private void handleSendGiveAudioStatusFailure() {
+ // TODO: Notify the failure status.
+
+ int uiCommand = mService.getSystemAudioMode()
+ ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON
+ : HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF
+ sendUserControlPressedAndReleased(uiCommand);
+ finish();
+ }
+
+ private void sendUserControlPressedAndReleased(int uiCommand) {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
+ mSourceAddress, mAvrLogicalAddress, uiCommand));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+ mSourceAddress, mAvrLogicalAddress));
+ }
+
+ @Override
+ final boolean processCommand(HdmiCecMessage cmd) {
+ switch (mState) {
+ case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
+ // TODO: Handle <FeatureAbort> of <SystemAudioModeRequest>
+ if (cmd.getOpcode() != HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE
+ || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
+ return false;
+ }
+ boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
+ if (receivedStatus == mTargetAudioStatus) {
+ setSystemAudioMode(receivedStatus);
+ sendGiveAudioStatus();
+ } else {
+ // Unexpected response, consider the request is newly initiated by AVR.
+ // To return 'false' will initiate new SystemAudioActionFromAvr by the control
+ // service.
+ finish();
+ return false;
+ }
+ return true;
+
+ case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
+ // TODO: Handle <FeatureAbort> of <GiveAudioStatus>
+ if (cmd.getOpcode() != HdmiCec.MESSAGE_REPORT_AUDIO_STATUS
+ || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
+ return false;
+ }
+ byte[] params = cmd.getParams();
+ if (params.length > 0) {
+ boolean mute = (params[0] & 0x80) == 0x80;
+ int volume = params[0] & 0x7F;
+ mService.setAudioStatus(mute, volume);
+ if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) {
+ // Toggle AVR's mute status to match with the system audio status.
+ sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
+ }
+ }
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ protected void removeSystemAudioActionInProgress() {
+ mService.removeActionExcept(SystemAudioActionFromTv.class, this);
+ mService.removeActionExcept(SystemAudioActionFromAvr.class, this);
+ }
+
+ @Override
+ final void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+ switch (mState) {
+ case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
+ handleSendSystemAudioModeRequestTimeout();
+ return;
+ case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
+ handleSendGiveAudioStatusFailure();
+ return;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
new file mode 100644
index 0000000..c5eb44b
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+/**
+ * Feature action that handles System Audio initiated by AVR devices.
+ */
+final class SystemAudioActionFromAvr extends SystemAudioAction {
+ /**
+ * Constructor
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param tvAddress logical address of TV device
+ * @param avrAddress logical address of AVR device
+ * @param targetStatus Whether to enable the system audio mode or not
+ * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
+ */
+ SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+ boolean targetStatus) {
+ super(service, tvAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ }
+
+ @Override
+ boolean start() {
+ removeSystemAudioActionInProgress();
+ handleSystemAudioActionFromAvr();
+ return true;
+ }
+
+ private void handleSystemAudioActionFromAvr() {
+ if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+ finish();
+ return;
+ }
+ if (mService.isInPresetInstallationMode()) {
+ sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ mSourceAddress, mAvrLogicalAddress,
+ HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
+ mTargetAudioStatus = false;
+ sendSystemAudioModeRequest();
+ return;
+ }
+ // TODO: Stop the action for System Audio Mode initialization if it is running.
+ if (mTargetAudioStatus) {
+ setSystemAudioMode(true);
+ sendGiveAudioStatus();
+ } else {
+ setSystemAudioMode(false);
+ finish();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
new file mode 100644
index 0000000..9994de6
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+
+/**
+ * Feature action that handles System Audio initiated by TV devices.
+ */
+final class SystemAudioActionFromTv extends SystemAudioAction {
+ /**
+ * Constructor
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param tvAddress logical address of TV device
+ * @param avrAddress logical address of AVR device
+ * @param targetStatus Whether to enable the system audio mode or not
+ * @throw IllegalArugmentException if device type of tvAddress is invalid
+ */
+ SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+ boolean targetStatus) {
+ super(service, tvAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ }
+
+ @Override
+ boolean start() {
+ // TODO: Check HDMI-CEC is enabled.
+ // TODO: Move to the waiting state if currently a routing change is in progress.
+
+ removeSystemAudioActionInProgress();
+ sendSystemAudioModeRequest();
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 51ee93b..2699fea 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -96,7 +96,7 @@ public class FlpHardwareProvider {
Looper.myLooper());
}
- public boolean isSupported() {
+ public static boolean isSupported() {
return nativeIsSupported();
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 386402b..05eb0f5 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1416,14 +1416,15 @@ public class NotificationManagerService extends SystemService {
}
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
+ final boolean isNotificationFromListener = mEnabledListenerPackageNames.contains(pkg);
final int userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
final UserHandle user = new UserHandle(userId);
// Limit the number of notifications that any given package except the android
- // package can enqueue. Prevents DOS attacks and deals with leaks.
- if (!isSystemNotification) {
+ // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
+ if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationList) {
int count = 0;
final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index bab4895..5081bf7 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -44,7 +44,7 @@ import java.util.Map;
* {@hide}
*/
public class NotificationUsageStats {
- private static final boolean ENABLE_SQLITE_LOG = false;
+ private static final boolean ENABLE_SQLITE_LOG = true;
// Guarded by synchronized(this).
private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>();
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 02f95e9..4ac2dcc 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -34,6 +34,8 @@ import java.util.LinkedList;
/**
* This {@link NotificationSignalExtractor} attempts to validate
* people references. Also elevates the priority of real people.
+ *
+ * {@hide}
*/
public class ValidateNotificationPeople implements NotificationSignalExtractor {
private static final String TAG = "ValidateNotificationPeople";
@@ -147,7 +149,8 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
};
}
- private String[] getExtraPeople(Bundle extras) {
+ // VisibleForTesting
+ public static String[] getExtraPeople(Bundle extras) {
Object people = extras.get(Notification.EXTRA_PEOPLE);
if (people instanceof String[]) {
return (String[]) people;
diff --git a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
index 85bde98..3d432dc 100644
--- a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -26,44 +26,48 @@ 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}.
+ * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
+ * If an {@link Intent} matches the {@link CrossProfileIntentFilter}, then activities in the user
+ * {@link #mTargetUserId} can access it.
*/
-class ForwardingIntentFilter extends IntentFilter {
- private static final String ATTR_USER_ID_DEST = "userIdDest";
+class CrossProfileIntentFilter extends IntentFilter {
+ private static final String ATTR_TARGET_USER_ID = "targetUserId";
+ private static final String ATTR_USER_ID_DEST = "userIdDest";//Old name. Kept for compatibility.
private static final String ATTR_REMOVABLE = "removable";
private static final String ATTR_FILTER = "filter";
- private static final String TAG = "ForwardingIntentFilter";
+ private static final String TAG = "CrossProfileIntentFilter";
// If the intent matches the IntentFilter, then it can be forwarded to this userId.
- final int mUserIdDest;
+ final int mTargetUserId;
boolean mRemovable;
- ForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdDest) {
+ CrossProfileIntentFilter(IntentFilter filter, boolean removable, int targetUserId) {
super(filter);
- mUserIdDest = userIdDest;
+ mTargetUserId = targetUserId;
mRemovable = removable;
}
- public int getUserIdDest() {
- return mUserIdDest;
+ public int getTargetUserId() {
+ return mTargetUserId;
}
public boolean isRemovable() {
return mRemovable;
}
- 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 " +
+ CrossProfileIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String targetUserIdString = parser.getAttributeValue(null, ATTR_TARGET_USER_ID);
+ if (targetUserIdString == null) {
+ targetUserIdString = parser.getAttributeValue(null, ATTR_USER_ID_DEST);
+ }
+ if (targetUserIdString == null) {
+ String msg = "Missing element under " + TAG +": " + ATTR_TARGET_USER_ID + " at " +
parser.getPositionDescription();
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- mUserIdDest = UserHandle.USER_NULL;
+ mTargetUserId = UserHandle.USER_NULL;
} else {
- mUserIdDest = Integer.parseInt(userIdDestString);
+ mTargetUserId = Integer.parseInt(targetUserIdString);
}
String removableString = parser.getAttributeValue(null, ATTR_REMOVABLE);
if (removableString != null) {
@@ -99,7 +103,7 @@ class ForwardingIntentFilter extends IntentFilter {
}
public void writeToXml(XmlSerializer serializer) throws IOException {
- serializer.attribute(null, ATTR_USER_ID_DEST, Integer.toString(mUserIdDest));
+ serializer.attribute(null, ATTR_TARGET_USER_ID, Integer.toString(mTargetUserId));
serializer.attribute(null, ATTR_REMOVABLE, Boolean.toString(mRemovable));
serializer.startTag(null, ATTR_FILTER);
super.writeToXml(serializer);
@@ -108,7 +112,7 @@ class ForwardingIntentFilter extends IntentFilter {
@Override
public String toString() {
- return "ForwardingIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this))
- + " " + Integer.toString(mUserIdDest) + "}";
+ return "CrossProfileIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this))
+ + " " + Integer.toString(mTargetUserId) + "}";
}
}
diff --git a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
index 1616395..a335d3a 100644
--- a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
@@ -23,22 +23,22 @@ import com.android.server.IntentResolver;
import java.util.List;
/**
- * Used to find a list of {@link ForwardingIntentFilter}s that match an intent.
+ * Used to find a list of {@link CrossProfileIntentFilter}s that match an intent.
*/
-class ForwardingIntentResolver
- extends IntentResolver<ForwardingIntentFilter, ForwardingIntentFilter> {
+class CrossProfileIntentResolver
+ extends IntentResolver<CrossProfileIntentFilter, CrossProfileIntentFilter> {
@Override
- protected ForwardingIntentFilter[] newArray(int size) {
- return new ForwardingIntentFilter[size];
+ protected CrossProfileIntentFilter[] newArray(int size) {
+ return new CrossProfileIntentFilter[size];
}
@Override
- protected boolean isPackageForFilter(String packageName, ForwardingIntentFilter filter) {
+ protected boolean isPackageForFilter(String packageName, CrossProfileIntentFilter filter) {
return false;
}
@Override
- protected void sortResults(List<ForwardingIntentFilter> results) {
+ protected void sortResults(List<CrossProfileIntentFilter> results) {
//We don't sort the results
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5e3325c..25ebfc0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -40,6 +40,7 @@ import android.util.Log;
import android.util.Slog;
import com.android.internal.content.PackageMonitor;
+import com.android.server.SystemService;
import java.util.ArrayList;
import java.util.List;
@@ -48,358 +49,374 @@ import java.util.List;
* Service that manages requests and callbacks for launchers that support
* 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;
- private final UserManager mUm;
- private final PackageCallbackList<IOnAppsChangedListener> mListeners
- = new PackageCallbackList<IOnAppsChangedListener>();
- private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+public class LauncherAppsService extends SystemService {
+
+ private final LauncherAppsImpl mLauncherAppsImpl;
public LauncherAppsService(Context context) {
- mContext = context;
- mPm = mContext.getPackageManager();
- mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ super(context);
+ mLauncherAppsImpl = new LauncherAppsImpl(context);
}
- /*
- * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
- * android.content.pm.IOnAppsChangedListener)
- */
@Override
- public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
- synchronized (mListeners) {
- if (DEBUG) {
- Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
- }
- if (mListeners.getRegisteredCallbackCount() == 0) {
+ public void onStart() {
+ publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
+ }
+
+ class LauncherAppsImpl extends ILauncherApps.Stub {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "LauncherAppsService";
+ private final Context mContext;
+ private final PackageManager mPm;
+ private final UserManager mUm;
+ private final PackageCallbackList<IOnAppsChangedListener> mListeners
+ = new PackageCallbackList<IOnAppsChangedListener>();
+
+ private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+
+ public LauncherAppsImpl(Context context) {
+ mContext = context;
+ mPm = mContext.getPackageManager();
+ mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+
+ /*
+ * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
+ * android.content.pm.IOnAppsChangedListener)
+ */
+ @Override
+ public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
+ synchronized (mListeners) {
if (DEBUG) {
- Log.d(TAG, "Starting package monitoring");
+ Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
}
- startWatchingPackageBroadcasts();
+ if (mListeners.getRegisteredCallbackCount() == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "Starting package monitoring");
+ }
+ startWatchingPackageBroadcasts();
+ }
+ mListeners.unregister(listener);
+ mListeners.register(listener, Binder.getCallingUserHandle());
}
- mListeners.unregister(listener);
- mListeners.register(listener, Binder.getCallingUserHandle());
}
- }
- /*
- * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
- * android.content.pm.IOnAppsChangedListener)
- */
- @Override
- 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();
+ /*
+ * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
+ * android.content.pm.IOnAppsChangedListener)
+ */
+ @Override
+ 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();
+ }
}
}
- }
-
- /**
- * Register a receiver to watch for package broadcasts
- */
- private void startWatchingPackageBroadcasts() {
- mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
- }
- /**
- * Unregister package broadcast receiver
- */
- private void stopWatchingPackageBroadcasts() {
- if (DEBUG) {
- Log.d(TAG, "Stopped watching for packages");
+ /**
+ * Register a receiver to watch for package broadcasts
+ */
+ private void startWatchingPackageBroadcasts() {
+ mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
}
- mPackageMonitor.unregister();
- }
- void checkCallbackCount() {
- synchronized (mListeners) {
+ /**
+ * Unregister package broadcast receiver
+ */
+ private void stopWatchingPackageBroadcasts() {
if (DEBUG) {
- Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount());
- }
- if (mListeners.getRegisteredCallbackCount() == 0) {
- stopWatchingPackageBroadcasts();
+ Log.d(TAG, "Stopped watching for packages");
}
+ mPackageMonitor.unregister();
}
- }
- /**
- * Checks if the caller is in the same group as the userToCheck.
- */
- private void ensureInUserProfiles(UserHandle userToCheck, String message) {
- final int callingUserId = UserHandle.getCallingUserId();
- final int targetUserId = userToCheck.getIdentifier();
-
- if (targetUserId == callingUserId) return;
-
- long ident = Binder.clearCallingIdentity();
- try {
- UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
- UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
- if (targetUserInfo == null
- || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
- || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
- throw new SecurityException(message);
+ void checkCallbackCount() {
+ synchronized (mListeners) {
+ if (DEBUG) {
+ Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount());
+ }
+ if (mListeners.getRegisteredCallbackCount() == 0) {
+ stopWatchingPackageBroadcasts();
+ }
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
- }
- /**
- * Checks if the user is enabled.
- */
- private boolean isUserEnabled(UserHandle user) {
- long ident = Binder.clearCallingIdentity();
- try {
- UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
- return targetUserInfo != null && targetUserInfo.isEnabled();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
+ /**
+ * Checks if the caller is in the same group as the userToCheck.
+ */
+ private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int targetUserId = userToCheck.getIdentifier();
- @Override
- public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return new ArrayList<ResolveInfo>();
- }
+ if (targetUserId == callingUserId) return;
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- mainIntent.setPackage(packageName);
- long ident = Binder.clearCallingIdentity();
- try {
- List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
- user.getIdentifier());
- return apps;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public ResolveInfo resolveActivity(Intent intent, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return null;
+ long ident = Binder.clearCallingIdentity();
+ try {
+ UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
+ UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
+ if (targetUserInfo == null
+ || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
+ || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
+ throw new SecurityException(message);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- long ident = Binder.clearCallingIdentity();
- try {
- ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
- return app;
- } finally {
- Binder.restoreCallingIdentity(ident);
+ /**
+ * Checks if the user is enabled.
+ */
+ private boolean isUserEnabled(UserHandle user) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
+ return targetUserInfo != null && targetUserInfo.isEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- @Override
- public boolean isPackageEnabled(String packageName, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return false;
- }
+ @Override
+ public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return new ArrayList<ResolveInfo>();
+ }
- 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);
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ mainIntent.setPackage(packageName);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
+ user.getIdentifier());
+ return apps;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- @Override
- public boolean isActivityEnabled(ComponentName component, UserHandle user)
- throws RemoteException {
- ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
- if (!isUserEnabled(user)) {
- return false;
- }
+ @Override
+ public ResolveInfo resolveActivity(Intent intent, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return null;
+ }
- 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);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
+ return app;
+ } 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);
- if (!isUserEnabled(user)) {
- throw new IllegalStateException("Cannot start activity for disabled profile " + user);
- }
+ @Override
+ public boolean isPackageEnabled(String packageName, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return false;
+ }
- Intent launchIntent = new Intent(Intent.ACTION_MAIN);
- launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.setComponent(component);
- launchIntent.setSourceBounds(sourceBounds);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- long ident = Binder.clearCallingIdentity();
- try {
- mContext.startActivityAsUser(launchIntent, opts, user);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ 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);
+ }
}
- }
- private class MyPackageMonitor extends PackageMonitor {
-
- /** Checks if user is a profile of or same as listeningUser.
- * and the user is enabled. */
- private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
- String debugMsg) {
- if (user.getIdentifier() == listeningUser.getIdentifier()) {
- if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
- return true;
+ @Override
+ public boolean isActivityEnabled(ComponentName component, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ return false;
}
+
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
- || !userInfo.isEnabled()) {
- 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;
- }
+ IPackageManager pm = AppGlobals.getPackageManager();
+ ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+ return info != null && info.isEnabled();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
- public void onPackageAdded(String packageName, int uid) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
- try {
- listener.onPackageAdded(user, packageName);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
+ public void startActivityAsUser(ComponentName component, Rect sourceBounds,
+ Bundle opts, UserHandle user) throws RemoteException {
+ ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+ if (!isUserEnabled(user)) {
+ throw new IllegalStateException("Cannot start activity for disabled profile " + user);
}
- mListeners.finishBroadcast();
- super.onPackageAdded(packageName, uid);
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.setComponent(component);
+ launchIntent.setSourceBounds(sourceBounds);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(launchIntent, opts, user);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
+ private class MyPackageMonitor extends PackageMonitor {
+
+ /** Checks if user is a profile of or same as listeningUser.
+ * and the user is enabled. */
+ private boolean isEnabledProfileOf(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 {
- listener.onPackageRemoved(user, packageName);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ 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
+ || !userInfo.isEnabled()) {
+ 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);
}
}
- mListeners.finishBroadcast();
- super.onPackageRemoved(packageName, uid);
- }
-
- @Override
- public void onPackageModified(String packageName) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
- try {
- listener.onPackageChanged(user, packageName);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
+ try {
+ listener.onPackageAdded(user, packageName);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
+ mListeners.finishBroadcast();
+
+ super.onPackageAdded(packageName, uid);
}
- mListeners.finishBroadcast();
- super.onPackageModified(packageName);
- }
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
+ try {
+ listener.onPackageRemoved(user, packageName);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ mListeners.finishBroadcast();
- @Override
- public void onPackagesAvailable(String[] packages) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
- try {
- listener.onPackagesAvailable(user, packages, isReplacing());
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ super.onPackageRemoved(packageName, uid);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
+ try {
+ listener.onPackageChanged(user, packageName);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
+ mListeners.finishBroadcast();
+
+ super.onPackageModified(packageName);
}
- mListeners.finishBroadcast();
- super.onPackagesAvailable(packages);
- }
+ @Override
+ public void onPackagesAvailable(String[] packages) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
+ try {
+ listener.onPackagesAvailable(user, packages, isReplacing());
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ mListeners.finishBroadcast();
- @Override
- public void onPackagesUnavailable(String[] packages) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
- try {
- listener.onPackagesUnavailable(user, packages, isReplacing());
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
+ super.onPackagesAvailable(packages);
+ }
+
+ @Override
+ public void onPackagesUnavailable(String[] packages) {
+ UserHandle user = new UserHandle(getChangingUserId());
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
+ try {
+ listener.onPackagesUnavailable(user, packages, isReplacing());
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
+ mListeners.finishBroadcast();
+
+ super.onPackagesUnavailable(packages);
}
- mListeners.finishBroadcast();
- super.onPackagesUnavailable(packages);
}
- }
-
- class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
- @Override
- public void onCallbackDied(T callback, Object cookie) {
- checkCallbackCount();
+ class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
+ @Override
+ public void onCallbackDied(T callback, Object cookie) {
+ checkCallbackCount();
+ }
}
}
-}
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f90d7ab..3ed73f7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -32,6 +32,7 @@ import android.content.pm.PackageParser.PackageLite;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
+import android.os.FileBridge;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -114,7 +115,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private boolean mPermissionsConfirmed;
private boolean mInvalid;
- private ArrayList<WritePipe> mPipes = new ArrayList<>();
+ private ArrayList<FileBridge> mBridges = new ArrayList<>();
private IPackageInstallObserver2 mRemoteObserver;
@@ -159,14 +160,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
- final WritePipe pipe;
+ final FileBridge bridge;
synchronized (mLock) {
if (!mMutationsAllowed) {
throw new IllegalStateException("Mutations not allowed");
}
- pipe = new WritePipe();
- mPipes.add(pipe);
+ bridge = new FileBridge();
+ mBridges.add(bridge);
}
try {
@@ -194,9 +195,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
- pipe.setTargetFd(targetFd);
- pipe.start();
- return pipe.getWriteFd();
+ bridge.setTargetFile(targetFd);
+ bridge.start();
+ return new ParcelFileDescriptor(bridge.getClientSocket());
} catch (ErrnoException e) {
throw new IllegalStateException("Failed to write", e);
@@ -218,8 +219,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Verify that all writers are hands-off
if (mMutationsAllowed) {
- for (WritePipe pipe : mPipes) {
- if (!pipe.isClosed()) {
+ for (FileBridge bridge : mBridges) {
+ if (!bridge.isClosed()) {
throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED,
"Files still open");
}
@@ -482,52 +483,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private static class WritePipe extends Thread {
- private final ParcelFileDescriptor[] mPipe;
-
- private FileDescriptor mTargetFd;
-
- private volatile boolean mClosed;
-
- public WritePipe() {
- try {
- mPipe = ParcelFileDescriptor.createPipe();
- } catch (IOException e) {
- throw new IllegalStateException("Failed to create pipe");
- }
- }
-
- public boolean isClosed() {
- return mClosed;
- }
-
- public void setTargetFd(FileDescriptor targetFd) {
- mTargetFd = targetFd;
- }
-
- public ParcelFileDescriptor getWriteFd() {
- return mPipe[1];
- }
-
- @Override
- public void run() {
- FileInputStream in = null;
- FileOutputStream out = null;
- try {
- // TODO: look at switching to sendfile(2) to speed up
- in = new FileInputStream(mPipe[0].getFileDescriptor());
- out = new FileOutputStream(mTargetFd);
- Streams.copy(in, out);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to stream data: " + e);
- } finally {
- IoUtils.closeQuietly(mPipe[0]);
- IoUtils.closeQuietly(mTargetFd);
- mClosed = true;
- }
- }
- }
-
private class InstallFailedException extends Exception {
private final int error;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d505e81..8585b4e 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -40,6 +40,7 @@ import com.android.internal.R;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.NativeLibraryHelper.ApkHandle;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
@@ -3374,25 +3375,26 @@ public class PackageManagerService extends IPackageManager.Stub {
* Returns if intent can be forwarded from the userId from to dest
*/
@Override
- public boolean canForwardTo(Intent intent, String resolvedType, int userIdFrom, int userIdDest) {
+ public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId,
+ int targetUserId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- List<ForwardingIntentFilter> matches =
- getMatchingForwardingIntentFilters(intent, resolvedType, userIdFrom);
+ List<CrossProfileIntentFilter> matches =
+ getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);
if (matches != null) {
int size = matches.size();
for (int i = 0; i < size; i++) {
- if (matches.get(i).getUserIdDest() == userIdDest) return true;
+ if (matches.get(i).getTargetUserId() == targetUserId) return true;
}
}
return false;
}
- private List<ForwardingIntentFilter> getMatchingForwardingIntentFilters(Intent intent,
+ private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId) {
- ForwardingIntentResolver fir = mSettings.mForwardingIntentResolvers.get(userId);
- if (fir != null) {
- return fir.queryIntent(intent, resolvedType, false, userId);
+ CrossProfileIntentResolver cpir = mSettings.mCrossProfileIntentResolvers.get(userId);
+ if (cpir != null) {
+ return cpir.queryIntent(intent, resolvedType, false, userId);
}
return null;
}
@@ -3428,31 +3430,31 @@ public class PackageManagerService extends IPackageManager.Stub {
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;
+ List<CrossProfileIntentFilter> cpifs =
+ getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
+ if (cpifs != null) {
+ CrossProfileIntentFilter crossProfileIntentFilterWithResult = 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
+ for (CrossProfileIntentFilter cpif : cpifs) {
+ int targetUserId = cpif.getTargetUserId();
+ // Two {@link CrossProfileIntentFilter}s can have the same targetUserId 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)) {
+ if (!alreadyTriedUserIds.contains(targetUserId)) {
List<ResolveInfo> resultUser = mActivities.queryIntent(intent,
- resolvedType, flags, userIdDest);
+ resolvedType, flags, targetUserId);
if (resultUser != null) {
- forwardingIntentFilterWithResult = fif;
+ crossProfileIntentFilterWithResult = cpif;
// As soon as there is a match in another user, we add the
// intentForwarderActivity to the list of ResolveInfo.
break;
}
- alreadyTriedUserIds.add(userIdDest);
+ alreadyTriedUserIds.add(targetUserId);
}
}
- if (forwardingIntentFilterWithResult != null) {
+ if (crossProfileIntentFilterWithResult != null) {
ResolveInfo forwardingResolveInfo = createForwardingResolveInfo(
- forwardingIntentFilterWithResult, userId);
+ crossProfileIntentFilterWithResult, userId);
result.add(forwardingResolveInfo);
}
}
@@ -3467,10 +3469,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private ResolveInfo createForwardingResolveInfo(ForwardingIntentFilter fif, int userIdFrom) {
+ private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter cpif,
+ int sourceUserId) {
String className;
- int userIdDest = fif.getUserIdDest();
- if (userIdDest == UserHandle.USER_OWNER) {
+ int targetUserId = cpif.getTargetUserId();
+ if (targetUserId == UserHandle.USER_OWNER) {
className = FORWARD_INTENT_TO_USER_OWNER;
} else {
className = FORWARD_INTENT_TO_MANAGED_PROFILE;
@@ -3478,14 +3481,14 @@ public class PackageManagerService extends IPackageManager.Stub {
ComponentName forwardingActivityComponentName = new ComponentName(
mAndroidApplication.packageName, className);
ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
- userIdFrom);
+ sourceUserId);
ResolveInfo forwardingResolveInfo = new ResolveInfo();
forwardingResolveInfo.activityInfo = forwardingActivityInfo;
forwardingResolveInfo.priority = 0;
forwardingResolveInfo.preferredOrder = 0;
forwardingResolveInfo.match = 0;
forwardingResolveInfo.isDefault = true;
- forwardingResolveInfo.filter = fif;
+ forwardingResolveInfo.filter = cpif;
return forwardingResolveInfo;
}
@@ -4146,7 +4149,7 @@ public class PackageManagerService extends IPackageManager.Stub {
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
- flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
+ flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null, null);
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -4213,7 +4216,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* Returns null in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile,
- int parseFlags, int scanMode, long currentTime, UserHandle user) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
String scanPath = scanFile.getPath();
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
@@ -4281,7 +4284,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
} else {
- // The current app on the system partion is better than
+ // The current app on the system partition is better than
// what we have updated to on the data partition; switch
// back to the system partition version.
// At this point, its safely assumed that package installation for
@@ -4400,7 +4403,7 @@ public class PackageManagerService extends IPackageManager.Stub {
setApplicationInfoPaths(pkg, codePath, resPath);
// 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);
+ | SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
/*
* If the system app should be overridden by a previously installed
@@ -4943,7 +4946,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
- int parseFlags, int scanMode, long currentTime, UserHandle user) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
File scanFile = new File(pkg.mScanPath);
if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
@@ -5393,7 +5396,22 @@ public class PackageManagerService extends IPackageManager.Stub {
* only for non-system apps and system app upgrades.
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
+ final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
try {
+ // Enable gross and lame hacks for apps that are built with old
+ // SDK tools. We must scan their APKs for renderscript bitcode and
+ // not launch them if it's present. Don't bother checking on devices
+ // that don't have 64 bit support.
+ String[] abiList = Build.SUPPORTED_ABIS;
+ boolean hasLegacyRenderscriptBitcode = false;
+ if (abiOverride != null) {
+ abiList = new String[] { abiOverride };
+ } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ hasLegacyRenderscriptBitcode = true;
+ }
+
File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
final String dataPathString = dataPath.getCanonicalPath();
@@ -5409,21 +5427,26 @@ public class PackageManagerService extends IPackageManager.Stub {
Log.i(TAG, "removed obsolete native libraries for system package "
+ path);
}
-
- setInternalAppAbi(pkg, pkgSetting);
+ if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
+ pkgSetting.cpuAbiString = abiList[0];
+ } else {
+ setInternalAppAbi(pkg, pkgSetting);
+ }
} else {
if (!isForwardLocked(pkg) && !isExternal(pkg)) {
/*
- * Update native library dir if it starts with
- * /data/data
- */
+ * Update native library dir if it starts with
+ * /data/data
+ */
if (nativeLibraryDir.getPath().startsWith(dataPathString)) {
setInternalAppNativeLibraryPath(pkg, pkgSetting);
nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
}
try {
- int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir);
+ int copyRet = copyNativeLibrariesForInternalApp(handle,
+ nativeLibraryDir, abiList);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
Slog.e(TAG, "Unable to copy native libraries");
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -5433,7 +5456,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// We've successfully copied native libraries across, so we make a
// note of what ABI we're using
if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[copyRet];
+ pkg.applicationInfo.cpuAbi = abiList[copyRet];
+ } else if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
} else {
pkg.applicationInfo.cpuAbi = null;
}
@@ -5450,20 +5475,22 @@ public class PackageManagerService extends IPackageManager.Stub {
// 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);
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
if (abi >= 0) {
- pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[abi];
+ pkg.applicationInfo.cpuAbi = abiList[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.cpuAbi = null;
+ if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
+ } else {
+ pkg.applicationInfo.cpuAbi = null;
+ }
} else {
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
}
- handle.close();
}
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
@@ -5480,8 +5507,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ pkgSetting.cpuAbiString = pkg.applicationInfo.cpuAbi;
} catch (IOException ioe) {
Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
+ } finally {
+ handle.close();
}
}
pkg.mScanPath = path;
@@ -6173,8 +6204,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir)
- throws IOException {
+ private static int copyNativeLibrariesForInternalApp(ApkHandle handle,
+ final File nativeLibraryDir, String[] abiList) throws IOException {
if (!nativeLibraryDir.isDirectory()) {
nativeLibraryDir.delete();
@@ -6196,21 +6227,16 @@ public class PackageManagerService extends IPackageManager.Stub {
* If this is an internal application or our nativeLibraryPath points to
* the app-lib directory, unpack the libraries if necessary.
*/
- final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
- try {
- int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
- if (abi >= 0) {
- int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
- nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
- if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
- return copyRet;
- }
+ int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+ if (abi >= 0) {
+ int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+ nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
+ if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+ return copyRet;
}
-
- return abi;
- } finally {
- handle.close();
}
+
+ return abi;
}
private void killApplication(String pkgName, int appId, String reason) {
@@ -7534,7 +7560,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
p = scanPackageLI(fullPath, flags,
SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
- System.currentTimeMillis(), UserHandle.ALL);
+ System.currentTimeMillis(), UserHandle.ALL, null);
if (p != null) {
/*
* TODO this seems dangerous as the package may have
@@ -7655,6 +7681,16 @@ public class PackageManagerService extends IPackageManager.Stub {
if (observer == null && observer2 == null) {
throw new IllegalArgumentException("No install observer supplied");
}
+ installPackageWithVerificationEncryptionAndAbiOverrideEtc(packageURI, observer, observer2,
+ flags, installerPackageName, verificationParams, encryptionParams, null);
+ }
+
+ @Override
+ public void installPackageWithVerificationEncryptionAndAbiOverrideEtc(Uri packageURI,
+ IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
+ int flags, String installerPackageName,
+ VerificationParams verificationParams, ContainerEncryptionParams encryptionParams,
+ String packageAbiOverride) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
@@ -7694,7 +7730,8 @@ public class PackageManagerService extends IPackageManager.Stub {
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags,
- installerPackageName, verificationParams, encryptionParams, user);
+ installerPackageName, verificationParams, encryptionParams, user,
+ packageAbiOverride);
mHandler.sendMessage(msg);
}
@@ -7792,13 +7829,9 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public boolean getApplicationBlockedSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true,
+ "getApplicationBlocked for user " + userId);
PackageSetting pkgSetting;
- final int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "getApplicationBlocked for user " + userId);
- }
long callingId = Binder.clearCallingIdentity();
try {
// writer
@@ -8403,11 +8436,14 @@ public class PackageManagerService extends IPackageManager.Stub {
private int mRet;
private File mTempPackage;
final ContainerEncryptionParams encryptionParams;
+ final String packageAbiOverride;
+ final String packageInstructionSetOverride;
InstallParams(Uri packageURI,
IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
int flags, String installerPackageName, VerificationParams verificationParams,
- ContainerEncryptionParams encryptionParams, UserHandle user) {
+ ContainerEncryptionParams encryptionParams, UserHandle user,
+ String packageAbiOverride) {
super(user);
this.mPackageURI = packageURI;
this.flags = flags;
@@ -8416,6 +8452,9 @@ public class PackageManagerService extends IPackageManager.Stub {
this.installerPackageName = installerPackageName;
this.verificationParams = verificationParams;
this.encryptionParams = encryptionParams;
+ this.packageAbiOverride = packageAbiOverride;
+ this.packageInstructionSetOverride = (packageAbiOverride == null) ?
+ packageAbiOverride : VMRuntime.getInstructionSet(packageAbiOverride);
}
@Override
@@ -8561,7 +8600,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Remote call to find out default install location
final String packageFilePath = packageFile.getAbsolutePath();
pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags,
- lowThreshold);
+ lowThreshold, packageAbiOverride);
/*
* If we have too little free space, try to free cache
@@ -8570,10 +8609,10 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
final long size = mContainerService.calculateInstalledSize(
- packageFilePath, isForwardLocked());
+ packageFilePath, isForwardLocked(), packageAbiOverride);
if (mInstaller.freeCache(size + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath,
- flags, lowThreshold);
+ flags, lowThreshold, packageAbiOverride);
}
/*
* The cache free must have deleted the file we
@@ -8993,11 +9032,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final ManifestDigest manifestDigest;
final UserHandle user;
final String instructionSet;
+ final String abiOverride;
InstallArgs(Uri packageURI,
IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
int flags, String installerPackageName, ManifestDigest manifestDigest,
- UserHandle user, String instructionSet) {
+ UserHandle user, String instructionSet, String abiOverride) {
this.packageURI = packageURI;
this.flags = flags;
this.observer = observer;
@@ -9006,6 +9046,7 @@ public class PackageManagerService extends IPackageManager.Stub {
this.manifestDigest = manifestDigest;
this.user = user;
this.instructionSet = instructionSet;
+ this.abiOverride = abiOverride;
}
abstract void createCopyFile();
@@ -9061,12 +9102,13 @@ public class PackageManagerService extends IPackageManager.Stub {
FileInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.observer2, params.flags,
params.installerPackageName, params.getManifestDigest(),
- params.getUser(), null /* instruction set */);
+ params.getUser(), params.packageInstructionSetOverride,
+ params.packageAbiOverride);
}
FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
String instructionSet) {
- super(null, null, null, 0, null, null, null, instructionSet);
+ super(null, null, null, 0, null, null, null, instructionSet, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
@@ -9075,7 +9117,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) {
- super(packageURI, null, null, 0, null, null, null, instructionSet);
+ super(packageURI, null, null, 0, null, null, null, instructionSet, null);
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -9179,14 +9221,26 @@ public class PackageManagerService extends IPackageManager.Stub {
NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
nativeLibraryFile.delete();
}
+
+ final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile);
+ String[] abiList = (abiOverride != null) ?
+ new String[] { abiOverride } : Build.SUPPORTED_ABIS;
try {
- int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ abiOverride == null &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ }
+
+ int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
return copyRet;
}
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ handle.close();
}
return ret;
@@ -9401,14 +9455,15 @@ public class PackageManagerService extends IPackageManager.Stub {
AsecInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.observer2, params.flags,
params.installerPackageName, params.getManifestDigest(),
- params.getUser(), null /* instruction set */);
+ params.getUser(), params.packageInstructionSetOverride,
+ params.packageAbiOverride);
}
AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
String instructionSet, boolean isExternal, boolean isForwardLocked) {
super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet);
+ null, null, null, instructionSet, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@@ -9420,7 +9475,7 @@ public class PackageManagerService extends IPackageManager.Stub {
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, instructionSet);
+ null, null, null, instructionSet, null);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
@@ -9429,7 +9484,7 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean isExternal, boolean isForwardLocked) {
super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet);
+ null, null, null, instructionSet, null);
this.cid = cid;
}
@@ -9441,7 +9496,7 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkExternalFreeStorage(packageURI, isFwdLocked());
+ return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -9467,7 +9522,8 @@ public class PackageManagerService extends IPackageManager.Stub {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(),
- RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked());
+ RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(),
+ abiOverride);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -9775,7 +9831,7 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private void installNewPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, UserHandle user,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
@@ -9803,7 +9859,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
- System.currentTimeMillis(), user);
+ System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -9830,7 +9886,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private void replacePackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, UserHandle user,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
PackageParser.Package oldPackage;
String pkgName = pkg.packageName;
@@ -9859,17 +9915,19 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, perUserInstalled, installerPackageName, res,
+ abiOverride);
} else {
replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, perUserInstalled, installerPackageName, res,
+ abiOverride);
}
}
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
int[] allUsers, boolean[] perUserInstalled,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
PackageParser.Package newPackage = null;
String pkgName = deletedPackage.packageName;
boolean deletedPkg = true;
@@ -9894,7 +9952,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Successfully deleted the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
- System.currentTimeMillis(), user);
+ System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -9923,7 +9981,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// Since we failed to install the new package we need to restore the old
// package that we deleted.
- if(deletedPkg) {
+ if (deletedPkg) {
if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
File restoreFile = new File(deletedPackage.mPath);
// Parse old package
@@ -9934,7 +9992,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
| SCAN_UPDATE_TIME;
if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
- origUpdateTime, null) == null) {
+ origUpdateTime, null, null) == null) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
return;
}
@@ -9954,7 +10012,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
int[] allUsers, boolean[] perUserInstalled,
- String installerPackageName, PackageInstalledInfo res) {
+ String installerPackageName, PackageInstalledInfo res, String abiOverride) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + deletedPackage);
PackageParser.Package newPackage = null;
@@ -10008,7 +10066,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Successfully disabled the old package. Now proceed with re-installation
res.returnCode = mLastScanError = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
+ newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -10042,7 +10100,7 @@ public class PackageManagerService extends IPackageManager.Stub {
removeInstalledPackageLI(newPackage, true);
}
// Add back the old system package
- scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
+ scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user, null);
// Restore the old system information in Settings
synchronized(mPackages) {
if (updatedSettings) {
@@ -10276,10 +10334,10 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
- installerPackageName, res);
+ installerPackageName, res, args.abiOverride);
} else {
installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user,
- installerPackageName, res);
+ installerPackageName, res, args.abiOverride);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
@@ -10696,7 +10754,7 @@ public class PackageManagerService extends IPackageManager.Stub {
parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath,
- parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
+ parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null, null);
if (newPkg == null) {
Slog.w(TAG, "Failed to restore system package:" + newPs.name
@@ -11475,33 +11533,34 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
- public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig,
- int userIdDest) {
+ public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
+ int sourceUserId, int targetUserId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
if (filter.countActions() == 0) {
- Slog.w(TAG, "Cannot set a forwarding intent filter with no filter actions");
+ Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
return;
}
synchronized (mPackages) {
- mSettings.editForwardingIntentResolverLPw(userIdOrig).addFilter(
- new ForwardingIntentFilter(filter, removable, userIdDest));
- mSettings.writePackageRestrictionsLPr(userIdOrig);
+ mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(
+ new CrossProfileIntentFilter(filter, removable, targetUserId));
+ mSettings.writePackageRestrictionsLPr(sourceUserId);
}
}
@Override
- public void clearForwardingIntentFilters(int userIdOrig) {
+ public void clearCrossProfileIntentFilters(int sourceUserId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
synchronized (mPackages) {
- ForwardingIntentResolver fir = mSettings.editForwardingIntentResolverLPw(userIdOrig);
- HashSet<ForwardingIntentFilter> set =
- new HashSet<ForwardingIntentFilter>(fir.filterSet());
- for (ForwardingIntentFilter fif : set) {
- if (fif.isRemovable()) fir.removeFilter(fif);
+ CrossProfileIntentResolver cpir =
+ mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
+ HashSet<CrossProfileIntentFilter> set =
+ new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
+ for (CrossProfileIntentFilter cpif : set) {
+ if (cpif.isRemovable()) cpir.removeFilter(cpif);
}
- mSettings.writePackageRestrictionsLPr(userIdOrig);
+ mSettings.writePackageRestrictionsLPr(sourceUserId);
}
}
@@ -12508,7 +12567,7 @@ public class PackageManagerService extends IPackageManager.Stub {
doGc = true;
synchronized (mInstallLock) {
final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
- 0, 0, null);
+ 0, 0, null, null);
// Scan the package
if (pkg != null) {
/*
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index d70c725..c78249b 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -52,25 +52,49 @@ public final class SELinuxMMAC {
private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
// Signature seinfo values read from policy.
- private static HashMap<Signature, Policy> sSigSeinfo =
- new HashMap<Signature, Policy>();
+ private static HashMap<Signature, Policy> sSigSeinfo = new HashMap<Signature, Policy>();
// Default seinfo read from policy.
private static String sDefaultSeinfo = null;
- // Locations of potential install policy files.
- private static final File[] INSTALL_POLICY_FILE = {
- new File(Environment.getDataDirectory(), "security/mac_permissions.xml"),
- new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
- null};
+ // Data policy override version file.
+ private static final String DATA_VERSION_FILE =
+ Environment.getDataDirectory() + "/security/current/selinux_version";
- // Location of seapp_contexts policy file.
- private static final String SEAPP_CONTEXTS_FILE = "/seapp_contexts";
+ // Base policy version file.
+ private static final String BASE_VERSION_FILE = "/selinux_version";
+
+ // Whether override security policies should be loaded.
+ private static final boolean USE_OVERRIDE_POLICY = useOverridePolicy();
+
+ // Data override mac_permissions.xml policy file.
+ private static final String DATA_MAC_PERMISSIONS =
+ Environment.getDataDirectory() + "/security/current/mac_permissions.xml";
+
+ // Base mac_permissions.xml policy file.
+ private static final String BASE_MAC_PERMISSIONS =
+ Environment.getRootDirectory() + "/etc/security/mac_permissions.xml";
+
+ // Determine which mac_permissions.xml file to use.
+ private static final String MAC_PERMISSIONS = USE_OVERRIDE_POLICY ?
+ DATA_MAC_PERMISSIONS : BASE_MAC_PERMISSIONS;
+
+ // Data override seapp_contexts policy file.
+ private static final String DATA_SEAPP_CONTEXTS =
+ Environment.getDataDirectory() + "/security/current/seapp_contexts";
+
+ // Base seapp_contexts policy file.
+ private static final String BASE_SEAPP_CONTEXTS = "/seapp_contexts";
+
+ // Determine which seapp_contexts file to use.
+ private static final String SEAPP_CONTEXTS = USE_OVERRIDE_POLICY ?
+ DATA_SEAPP_CONTEXTS : BASE_SEAPP_CONTEXTS;
// Stores the hash of the last used seapp_contexts file.
private static final String SEAPP_HASH_FILE =
Environment.getDataDirectory().toString() + "/system/seapp_hash";
+
// Signature policy stanzas
static class Policy {
private String seinfo;
@@ -112,51 +136,17 @@ public final class SELinuxMMAC {
sDefaultSeinfo = null;
}
- /**
- * Parses an MMAC install policy from a predefined list of locations.
- * @return boolean indicating whether an install policy was correctly parsed.
- */
public static boolean readInstallPolicy() {
-
- return readInstallPolicy(INSTALL_POLICY_FILE);
- }
-
- /**
- * Parses an MMAC install policy given as an argument.
- * @param policyFile object representing the path of the policy.
- * @return boolean indicating whether the install policy was correctly parsed.
- */
- public static boolean readInstallPolicy(File policyFile) {
-
- return readInstallPolicy(new File[]{policyFile,null});
- }
-
- private static boolean readInstallPolicy(File[] policyFiles) {
// Temp structures to hold the rules while we parse the xml file.
// We add all the rules together once we know there's no structural problems.
HashMap<Signature, Policy> sigSeinfo = new HashMap<Signature, Policy>();
String defaultSeinfo = null;
FileReader policyFile = null;
- int i = 0;
- while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
- try {
- policyFile = new FileReader(policyFiles[i]);
- break;
- } catch (FileNotFoundException e) {
- Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
- }
- i++;
- }
-
- if (policyFile == null) {
- Slog.d(TAG, "No policy file found. All seinfo values will be null.");
- return false;
- }
-
- Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
-
try {
+ policyFile = new FileReader(MAC_PERMISSIONS);
+ Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS);
+
XmlPullParser parser = Xml.newPullParser();
parser.setInput(policyFile);
@@ -199,20 +189,14 @@ public final class SELinuxMMAC {
XmlUtils.skipCurrentTag(parser);
}
}
- } catch (XmlPullParserException e) {
- // An error outside of a stanza means a structural problem
- // with the xml file. So ignore it.
- Slog.w(TAG, "Got exception parsing ", e);
+ } catch (XmlPullParserException xpe) {
+ Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, xpe);
return false;
- } catch (IOException e) {
- Slog.w(TAG, "Got exception parsing ", e);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, ioe);
return false;
} finally {
- try {
- policyFile.close();
- } catch (IOException e) {
- //omit
- }
+ IoUtils.closeQuietly(policyFile);
}
flushInstallPolicy();
@@ -412,7 +396,7 @@ public final class SELinuxMMAC {
// Any error with the seapp_contexts file should be fatal
byte[] currentHash = null;
try {
- currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ currentHash = returnHash(SEAPP_CONTEXTS);
} catch (IOException ioe) {
Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
return false;
@@ -434,7 +418,7 @@ public final class SELinuxMMAC {
*/
public static void setRestoreconDone() {
try {
- final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+ final byte[] currentHash = returnHash(SEAPP_CONTEXTS);
dumpHash(new File(SEAPP_HASH_FILE), currentHash);
} catch (IOException ioe) {
Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
@@ -485,4 +469,21 @@ public final class SELinuxMMAC {
throw new RuntimeException(nsae); // impossible
}
}
+
+ private static boolean useOverridePolicy() {
+ try {
+ final String overrideVersion = IoUtils.readFileAsString(DATA_VERSION_FILE);
+ final String baseVersion = IoUtils.readFileAsString(BASE_VERSION_FILE);
+ if (overrideVersion.equals(baseVersion)) {
+ return true;
+ }
+ Slog.e(TAG, "Override policy version '" + overrideVersion + "' doesn't match " +
+ "base version '" + baseVersion + "'. Skipping override policy files.");
+ } catch (FileNotFoundException fnfe) {
+ // Override version file doesn't have to exist so silently ignore.
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Skipping override policy files.", ioe);
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3ca658f..3483fae 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -132,6 +132,9 @@ 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_CROSS_PROFILE_INTENT_FILTERS =
+ "crossProfile-intent-filters";
+ //Old name. Kept for compatibility
static final String TAG_FORWARDING_INTENT_FILTERS =
"forwarding-intent-filters";
@@ -189,8 +192,8 @@ final class Settings {
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 SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
+ new SparseArray<CrossProfileIntentResolver>();
final HashMap<String, SharedUserSetting> mSharedUsers =
new HashMap<String, SharedUserSetting>();
@@ -856,13 +859,13 @@ final class Settings {
return ppir;
}
- ForwardingIntentResolver editForwardingIntentResolverLPw(int userId) {
- ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId);
- if (fir == null) {
- fir = new ForwardingIntentResolver();
- mForwardingIntentResolvers.put(userId, fir);
+ CrossProfileIntentResolver editCrossProfileIntentResolverLPw(int userId) {
+ CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId);
+ if (cpir == null) {
+ cpir = new CrossProfileIntentResolver();
+ mCrossProfileIntentResolvers.put(userId, cpir);
}
- return fir;
+ return cpir;
}
private File getUserPackagesStateFile(int userId) {
@@ -980,7 +983,7 @@ final class Settings {
}
}
- private void readForwardingIntentFiltersLPw(XmlPullParser parser, int userId)
+ private void readCrossProfileIntentFiltersLPw(XmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -991,10 +994,10 @@ final class Settings {
}
String tagName = parser.getName();
if (tagName.equals(TAG_ITEM)) {
- ForwardingIntentFilter fif = new ForwardingIntentFilter(parser);
- editForwardingIntentResolverLPw(userId).addFilter(fif);
+ CrossProfileIntentFilter cpif = new CrossProfileIntentFilter(parser);
+ editCrossProfileIntentResolverLPw(userId).addFilter(cpif);
} else {
- String msg = "Unknown element under " + TAG_FORWARDING_INTENT_FILTERS + ": " +
+ String msg = "Unknown element under " + TAG_CROSS_PROFILE_INTENT_FILTERS + ": " +
parser.getName();
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
XmlUtils.skipCurrentTag(parser);
@@ -1130,8 +1133,9 @@ 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 if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)
+ || tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
+ readCrossProfileIntentFiltersLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -1209,18 +1213,18 @@ final class Settings {
serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES);
}
- void writeForwardingIntentFiltersLPr(XmlSerializer serializer, int userId)
+ void writeCrossProfileIntentFiltersLPr(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_CROSS_PROFILE_INTENT_FILTERS);
+ CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId);
+ if (cpir != null) {
+ for (final CrossProfileIntentFilter cpif : cpir.filterSet()) {
serializer.startTag(null, TAG_ITEM);
- fif.writeToXml(serializer);
+ cpif.writeToXml(serializer);
serializer.endTag(null, TAG_ITEM);
}
}
- serializer.endTag(null, TAG_FORWARDING_INTENT_FILTERS);
+ serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
}
void writePackageRestrictionsLPr(int userId) {
@@ -1321,7 +1325,7 @@ final class Settings {
writePersistentPreferredActivitiesLPr(serializer, userId);
- writeForwardingIntentFiltersLPr(serializer, userId);
+ writeCrossProfileIntentFiltersLPr(serializer, userId);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
@@ -1940,10 +1944,11 @@ 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)) {
+ } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)
+ || tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
// TODO: check whether this is okay! as it is very
// similar to how preferred-activities are treated
- readForwardingIntentFiltersLPw(parser, 0);
+ readCrossProfileIntentFiltersLPw(parser, 0);
} else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
@@ -2935,6 +2940,26 @@ final class Settings {
file.delete();
file = getUserPackagesStateBackupFile(userId);
file.delete();
+ removeCrossProfileIntentFiltersToUserLPr(userId);
+ }
+
+ void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) {
+ for (int i = 0; i < mCrossProfileIntentResolvers.size(); i++) {
+ int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
+ CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
+ boolean needsWriting = false;
+ HashSet<CrossProfileIntentFilter> cpifs =
+ new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
+ for (CrossProfileIntentFilter cpif : cpifs) {
+ if (cpif.getTargetUserId() == targetUserId) {
+ needsWriting = true;
+ cpir.removeFilter(cpif);
+ }
+ }
+ if (needsWriting) {
+ writePackageRestrictionsLPr(sourceUserId);
+ }
+ }
}
// This should be called (at least) whenever an application is removed
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 102b2d4..7162683 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -107,6 +107,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_TYPE_STRING_ARRAY = "sa";
private static final String ATTR_TYPE_STRING = "s";
private static final String ATTR_TYPE_BOOLEAN = "b";
+ private static final String ATTR_TYPE_INTEGER = "i";
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
@@ -1140,53 +1141,57 @@ public class UserManagerService extends IUserManager.Stub {
*/
public boolean removeUser(int userHandle) {
checkManageUsersPermission("Only the system can remove users");
- final UserInfo user;
- synchronized (mPackagesLock) {
- user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null) {
- return false;
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final UserInfo user;
+ synchronized (mPackagesLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
+ mRemovingUserIds.put(userHandle, true);
+ try {
+ mAppOpsService.removeUser(userHandle);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
+ }
+ // Set this to a partially created user, so that the user will be purged
+ // 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 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && user.isManagedProfile()) {
+ // Send broadcast to notify system that the user removed was a
+ // managed user.
+ sendProfileRemovedBroadcast(user.profileGroupId, user.id);
}
- mRemovingUserIds.put(userHandle, true);
+
+ if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
+ int res;
try {
- mAppOpsService.removeUser(userHandle);
+ res = ActivityManagerNative.getDefault().stopUser(userHandle,
+ new IStopUserCallback.Stub() {
+ @Override
+ public void userStopped(int userId) {
+ finishRemoveUser(userId);
+ }
+ @Override
+ public void userStopAborted(int userId) {
+ }
+ });
} catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
+ return false;
}
- // Set this to a partially created user, so that the user will be purged
- // 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 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
- && user.isManagedProfile()) {
- // Send broadcast to notify system that the user removed was a
- // managed user.
- sendProfileRemovedBroadcast(user.profileGroupId, user.id);
- }
-
- if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
- int res;
- try {
- res = ActivityManagerNative.getDefault().stopUser(userHandle,
- new IStopUserCallback.Stub() {
- @Override
- public void userStopped(int userId) {
- finishRemoveUser(userId);
- }
- @Override
- public void userStopAborted(int userId) {
- }
- });
- } catch (RemoteException e) {
- return false;
+ return res == ActivityManager.USER_OP_SUCCESS;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
-
- return res == ActivityManager.USER_OP_SUCCESS;
}
void finishRemoveUser(final int userHandle) {
@@ -1528,16 +1533,18 @@ public class UserManagerService extends IUserManager.Stub {
String [] valueStrings = new String[values.size()];
values.toArray(valueStrings);
restrictions.putStringArray(key, valueStrings);
- } else if (ATTR_TYPE_BOOLEAN.equals(valType)) {
- restrictions.putBoolean(key, Boolean.parseBoolean(
- parser.nextText().trim()));
} else {
String value = parser.nextText().trim();
- restrictions.putString(key, value);
+ if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+ restrictions.putBoolean(key, Boolean.parseBoolean(value));
+ } else if (ATTR_TYPE_INTEGER.equals(valType)) {
+ restrictions.putInt(key, Integer.parseInt(value));
+ } else {
+ restrictions.putString(key, value);
+ }
}
}
}
-
} catch (IOException ioe) {
} catch (XmlPullParserException pe) {
} finally {
@@ -1577,6 +1584,9 @@ public class UserManagerService extends IUserManager.Stub {
if (value instanceof Boolean) {
serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
serializer.text(value.toString());
+ } else if (value instanceof Integer) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
+ serializer.text(value.toString());
} else if (value == null || value instanceof String) {
serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
serializer.text(value != null ? (String) value : "");
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 462b234..32546df 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -40,6 +40,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -337,8 +338,12 @@ public class TrustManagerService extends SystemService {
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onTrustChanged(enabled, userId);
+ } catch (DeadObjectException e) {
+ if (DEBUG) Slog.d(TAG, "Removing dead TrustListener.");
+ mTrustListeners.remove(i);
+ i--;
} catch (RemoteException e) {
- Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e);
+ Slog.e(TAG, "Exception while notifying TrustListener.", e);
}
}
}
diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index 3c960c7..55dd4ab 100644
--- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -40,12 +40,20 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
private static final String fileContextsPath = "file_contexts";
private static final String propertyContextsPath = "property_contexts";
private static final String seappContextsPath = "seapp_contexts";
+ private static final String versionPath = "selinux_version";
+ private static final String macPermissionsPath = "mac_permissions.xml";
public SELinuxPolicyInstallReceiver() {
super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version");
}
private void backupContexts(File contexts) {
+ new File(contexts, versionPath).renameTo(
+ new File(contexts, versionPath + "_backup"));
+
+ new File(contexts, macPermissionsPath).renameTo(
+ new File(contexts, macPermissionsPath + "_backup"));
+
new File(contexts, seappContextsPath).renameTo(
new File(contexts, seappContextsPath + "_backup"));
@@ -60,6 +68,8 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
}
private void copyUpdate(File contexts) {
+ new File(updateDir, versionPath).renameTo(new File(contexts, versionPath));
+ new File(updateDir, macPermissionsPath).renameTo(new File(contexts, macPermissionsPath));
new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath));
new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath));
new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath));
@@ -75,11 +85,13 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
}
private int[] readChunkLengths(BufferedInputStream bundle) throws IOException {
- int[] chunks = new int[4];
+ int[] chunks = new int[6];
chunks[0] = readInt(bundle);
chunks[1] = readInt(bundle);
chunks[2] = readInt(bundle);
chunks[3] = readInt(bundle);
+ chunks[4] = readInt(bundle);
+ chunks[5] = readInt(bundle);
return chunks;
}
@@ -94,10 +106,12 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent));
try {
int[] chunkLengths = readChunkLengths(stream);
- installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]);
- installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]);
- installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]);
- installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]);
+ installFile(new File(updateDir, versionPath), stream, chunkLengths[0]);
+ installFile(new File(updateDir, macPermissionsPath), stream, chunkLengths[1]);
+ installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[2]);
+ installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[3]);
+ installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[4]);
+ installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[5]);
} finally {
IoUtils.closeQuietly(stream);
}
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 35d19c1..29bab22 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -21,6 +21,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.view.Display;
import android.view.Surface;
@@ -32,6 +33,10 @@ class CircularDisplayMask {
private static final String TAG = "CircularDisplayMask";
private static final int STROKE_WIDTH = 2;
+ // half the screen size
+ private static final int CIRCLE_RADIUS = 160;
+ // size of the chin
+ private static final int SCREEN_OFFSET = 30;
private final SurfaceControl mSurfaceControl;
private final Surface mSurface = new Surface();
@@ -40,12 +45,13 @@ class CircularDisplayMask {
private boolean mDrawNeeded;
private Paint mPaint;
private int mRotation;
+ private boolean mVisible;
public CircularDisplayMask(Display display, SurfaceSession session, int zOrder) {
SurfaceControl ctrl = null;
try {
ctrl = new SurfaceControl(session, "CircularDisplayMask",
- 320, 290, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ 320, 320, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(zOrder);
ctrl.setPosition(0, 0);
@@ -63,12 +69,12 @@ class CircularDisplayMask {
}
private void drawIfNeeded() {
- if (!mDrawNeeded) {
+ if (!mDrawNeeded || !mVisible) {
return;
}
mDrawNeeded = false;
- Rect dirty = new Rect(0, 0, mLastDW, mLastDH);
+ Rect dirty = new Rect(0, 0, 320, 320);
Canvas c = null;
try {
c = mSurface.lockCanvas(dirty);
@@ -78,27 +84,23 @@ class CircularDisplayMask {
if (c == null) {
return;
}
- int cx = 160;
- int cy = 160;
+ c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
switch (mRotation) {
- case Surface.ROTATION_0:
- case Surface.ROTATION_90:
- // chin bottom or right
- cx = 160;
- cy = 160;
- break;
- case Surface.ROTATION_180:
- // chin top
- cx = 160;
- cy = 145;
- break;
- case Surface.ROTATION_270:
- cx = 145;
- cy = 160;
- break;
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_90:
+ // chin bottom or right
+ mSurfaceControl.setPosition(0, 0);
+ break;
+ case Surface.ROTATION_180:
+ // chin top
+ mSurfaceControl.setPosition(0, -SCREEN_OFFSET);
+ break;
+ case Surface.ROTATION_270:
+ // chin left
+ mSurfaceControl.setPosition(-SCREEN_OFFSET, 0);
+ break;
}
- c.drawCircle(cx, cy, 160, mPaint);
-
+ c.drawCircle(CIRCLE_RADIUS, CIRCLE_RADIUS, CIRCLE_RADIUS, mPaint);
mSurface.unlockCanvasAndPost(c);
}
@@ -108,6 +110,7 @@ class CircularDisplayMask {
if (mSurfaceControl == null) {
return;
}
+ mVisible = on;
drawIfNeeded();
if (on) {
mSurfaceControl.show();
@@ -117,14 +120,14 @@ class CircularDisplayMask {
}
void positionSurface(int dw, int dh, int rotation) {
- if (mLastDW == dw && mLastDH == dh) {
+ if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
return;
}
mLastDW = dw;
mLastDH = dh;
- mSurfaceControl.setSize(dw, dh);
mDrawNeeded = true;
mRotation = rotation;
+ drawIfNeeded();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index da584d8..616db42 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5829,7 +5829,9 @@ public class WindowManagerService extends IWindowManager.Stub
// whether the screenshot should use the identity transformation matrix
// (e.g., enable it when taking a screenshot for recents, since we might be in
// the middle of the rotation animation, but don't want a rotated recent image).
- rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer, false);
+ // TODO: Replace 'new Rect()' with the portion of the screen to capture for the
+ // screenshot.
+ rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer, false);
}
} while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " +
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 163225e..9a5079d 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -18,6 +18,7 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "android/graphics/GraphicsJNI.h"
#include <android_view_GraphicBuffer.h>
#include <cutils/log.h>
@@ -46,7 +47,7 @@ namespace android {
// ----------------------------------------------------------------------------
static struct {
- jmethodID safeCanvasSwap;
+ jmethodID setNativeBitmap;
} gCanvasClassInfo;
#define INVOKEV(object, method, ...) \
@@ -63,9 +64,7 @@ static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, job
bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
bitmap->allocPixels();
bitmap->eraseColor(0);
-
- SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap));
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
return reinterpret_cast<jlong>(bitmap);
}
@@ -74,8 +73,7 @@ static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobj
jobject canvas, jlong bitmapHandle) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkCanvas* nativeCanvas = SkNEW(SkCanvas);
- INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+ INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
delete bitmap;
}
@@ -244,7 +242,7 @@ int register_android_server_AssetAtlasService(JNIEnv* env) {
jclass clazz;
FIND_CLASS(clazz, "android/graphics/Canvas");
- GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
+ GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index 6c14887..c2fccc1 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -139,6 +139,8 @@ static int SetThreadEvent(ThreadEvent event) {
* the HW module and obtaining the proper interfaces.
*/
static void ClassInit(JNIEnv* env, jclass clazz) {
+ sFlpInterface = NULL;
+
// get references to the Java provider methods
sOnLocationReport = env->GetMethodID(
clazz,
@@ -163,6 +165,38 @@ static void ClassInit(JNIEnv* env, jclass clazz) {
sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
+
+ // open the hardware module
+ const hw_module_t* module = NULL;
+ int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
+ if (err != 0) {
+ ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+ return;
+ }
+
+ err = module->methods->open(
+ module,
+ FUSED_LOCATION_HARDWARE_MODULE_ID,
+ &sHardwareDevice);
+ if (err != 0) {
+ ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+ return;
+ }
+
+ // acquire the interfaces pointers
+ flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
+ sFlpInterface = flp_device->get_flp_interface(flp_device);
+
+ if (sFlpInterface != NULL) {
+ sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
+ sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE));
+
+ sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
+ sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE));
+
+ sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
+ sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE));
+ }
}
/*
@@ -637,44 +671,6 @@ FlpGeofenceCallbacks sFlpGeofenceCallbacks = {
* the Flp interfaces are initialized properly.
*/
static void Init(JNIEnv* env, jobject obj) {
- if(sHardwareDevice != NULL) {
- ALOGD("Hardware Device already opened.");
- return;
- }
-
- const hw_module_t* module = NULL;
- int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
- if(err != 0) {
- ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
- return;
- }
-
- err = module->methods->open(
- module,
- FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice);
- if(err != 0) {
- ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
- return;
- }
-
- sFlpInterface = NULL;
- flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
- sFlpInterface = flp_device->get_flp_interface(flp_device);
-
- if(sFlpInterface != NULL) {
- sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
- sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE)
- );
-
- sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
- sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE)
- );
-
- sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
- sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE)
- );
- }
-
if(sCallbacksObj == NULL) {
sCallbacksObj = env->NewGlobalRef(obj);
}
@@ -696,7 +692,10 @@ static void Init(JNIEnv* env, jobject obj) {
}
static jboolean IsSupported(JNIEnv* env, jclass clazz) {
- return sFlpInterface != NULL;
+ if (sFlpInterface == NULL) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
}
static jint GetBatchSize(JNIEnv* env, jobject object) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5395f60..7381e5e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -130,6 +130,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final boolean DBG = false;
+ private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
+
final Context mContext;
final UserManager mUserManager;
final PowerManager.WakeLock mWakeLock;
@@ -190,6 +192,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// This is the list of component allowed to start lock task mode.
final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+ ComponentName mRestrictionsProvider;
+
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
@@ -836,7 +840,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(),
null, result, mHandler, Activity.RESULT_OK, null, null);
} else {
- mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ mContext.sendBroadcastAsUser(intent, admin.getUserHandle());
}
}
@@ -944,6 +948,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.startDocument(null, true);
out.startTag(null, "policies");
+ if (policy.mRestrictionsProvider != null) {
+ out.attribute(null, ATTR_PERMISSION_PROVIDER,
+ policy.mRestrictionsProvider.flattenToString());
+ }
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
@@ -1039,6 +1047,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
throw new XmlPullParserException(
"Settings do not start with policies tag: found " + tag);
}
+
+ // Extract the permission provider component name if available
+ String permissionProvider = parser.getAttributeValue(null, ATTR_PERMISSION_PROVIDER);
+ if (permissionProvider != null) {
+ policy.mRestrictionsProvider = ComponentName.unflattenFromString(permissionProvider);
+ }
+
type = parser.next();
int outerDepth = parser.getDepth();
policy.mLockTaskComponents.clear();
@@ -3108,6 +3123,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId()));
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
Intent.FLAG_RECEIVER_FOREGROUND);
+ // TODO This should send to parent of profile (which is always owner at the moment).
mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
} finally {
restoreCallingIdentity(id);
@@ -3303,7 +3319,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void addForwardingIntentFilter(ComponentName who, IntentFilter filter, int flags) {
+ @Override
+ public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ int userHandle = UserHandle.getCallingUserId();
+ DevicePolicyData userData = getUserData(userHandle);
+ userData.mRestrictionsProvider = permissionProvider;
+ saveSettingsLocked(userHandle);
+ }
+ }
+
+ @Override
+ public ComponentName getRestrictionsProvider(int userHandle) {
+ synchronized (this) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can query the permission provider");
+ }
+ DevicePolicyData userData = getUserData(userHandle);
+ return userData != null ? userData.mRestrictionsProvider : null;
+ }
+ }
+
+ public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
@@ -3314,12 +3356,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
IPackageManager pm = AppGlobals.getPackageManager();
long id = Binder.clearCallingIdentity();
try {
- if ((flags & DevicePolicyManager.FLAG_TO_PRIMARY_USER) != 0) {
- pm.addForwardingIntentFilter(filter, true /*removable*/, callingUserId,
+ if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) {
+ pm.addCrossProfileIntentFilter(filter, true /*removable*/, callingUserId,
UserHandle.USER_OWNER);
}
- if ((flags & DevicePolicyManager.FLAG_TO_MANAGED_PROFILE) != 0) {
- pm.addForwardingIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER,
+ if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) {
+ pm.addCrossProfileIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER,
callingUserId);
}
} catch (RemoteException re) {
@@ -3330,7 +3372,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void clearForwardingIntentFilters(ComponentName who) {
+ public void clearCrossProfileIntentFilters(ComponentName who) {
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
@@ -3340,8 +3382,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
IPackageManager pm = AppGlobals.getPackageManager();
long id = Binder.clearCallingIdentity();
try {
- pm.clearForwardingIntentFilters(callingUserId);
- pm.clearForwardingIntentFilters(UserHandle.USER_OWNER);
+ pm.clearCrossProfileIntentFilters(callingUserId);
+ pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER);
} catch (RemoteException re) {
// Shouldn't happen
} finally {
@@ -3514,111 +3556,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public void enableSystemApp(ComponentName who, String packageName) {
- synchronized (this) {
- if (who == null) {
- throw new NullPointerException("ComponentName is null");
- }
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
- int userId = UserHandle.getCallingUserId();
- long id = Binder.clearCallingIdentity();
-
- try {
- UserManager um = UserManager.get(mContext);
- if (!um.getUserInfo(userId).isManagedProfile()) {
- throw new IllegalStateException(
- "Only call this method from a managed profile.");
- }
-
- // TODO: Use UserManager::getProfileParent when available.
- UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER);
-
- if (DBG) {
- Slog.v(LOG_TAG, "installing " + packageName + " for "
- + userId);
- }
-
- IPackageManager pm = AppGlobals.getPackageManager();
- if (!isSystemApp(pm, packageName, primaryUser.id)) {
- throw new IllegalArgumentException("Only system apps can be enabled this way.");
- }
-
- // Install the app.
- pm.installExistingPackageAsUser(packageName, userId);
-
- } catch (RemoteException re) {
- // shouldn't happen
- Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
- } finally {
- restoreCallingIdentity(id);
- }
- }
- }
-
- @Override
- public int enableSystemAppWithIntent(ComponentName who, Intent intent) {
- synchronized (this) {
- if (who == null) {
- throw new NullPointerException("ComponentName is null");
- }
-
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
- int userId = UserHandle.getCallingUserId();
- long id = Binder.clearCallingIdentity();
-
- try {
- UserManager um = UserManager.get(mContext);
- if (!um.getUserInfo(userId).isManagedProfile()) {
- throw new IllegalStateException(
- "Only call this method from a managed profile.");
- }
-
- // TODO: Use UserManager::getProfileParent when available.
- UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER);
-
- IPackageManager pm = AppGlobals.getPackageManager();
- List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- 0, // no flags
- primaryUser.id);
-
- if (DBG) Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
- int numberOfAppsInstalled = 0;
- if (activitiesToEnable != null) {
- for (ResolveInfo info : activitiesToEnable) {
- if (info.activityInfo != null) {
-
- if (!isSystemApp(pm, info.activityInfo.packageName, primaryUser.id)) {
- throw new IllegalArgumentException(
- "Only system apps can be enabled this way.");
- }
-
-
- numberOfAppsInstalled++;
- pm.installExistingPackageAsUser(info.activityInfo.packageName, userId);
- }
- }
- }
- return numberOfAppsInstalled;
- } catch (RemoteException e) {
- // shouldn't happen
- Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
- return 0;
- } finally {
- restoreCallingIdentity(id);
- }
- }
- }
-
- private boolean isSystemApp(IPackageManager pm, String packageName, int userId)
- throws RemoteException {
- ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId);
- return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
- }
-
- @Override
public void setAccountManagementDisabled(ComponentName who, String accountType,
boolean disabled) {
if (!mHasFeature) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e8b7b69..18ece5b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,6 +80,7 @@ import com.android.server.pm.PackageManagerService;
import com.android.server.pm.UserManagerService;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
+import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.search.SearchManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
@@ -126,7 +127,7 @@ public final class SystemServer {
private static final String WIFI_SERVICE_CLASS =
"com.android.server.wifi.WifiService";
private static final String WIFI_PASSPOINT_SERVICE_CLASS =
- "com.android.server.wifi.passpoint.PasspointService";
+ "com.android.server.wifi.passpoint.WifiPasspointService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
private static final String HDMI_CEC_SERVICE_CLASS =
@@ -330,7 +331,6 @@ public final class SystemServer {
IPackageManager pm = null;
WindowManagerService wm = null;
BluetoothManagerService bluetooth = null;
- DockObserver dock = null;
UsbService usb = null;
SerialService serial = null;
RecognitionManagerService recognition = null;
@@ -644,15 +644,15 @@ public final class SystemServer {
}
try {
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting Wi-Fi Service", e);
+ reportWtf("starting Wi-Fi PasspointService", e);
}
try {
- mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting Wi-Fi PasspointService", e);
+ reportWtf("starting Wi-Fi Service", e);
}
try {
@@ -789,13 +789,7 @@ public final class SystemServer {
}
if (!disableNonCoreServices) {
- try {
- Slog.i(TAG, "Dock Observer");
- // Listen for dock station changes
- dock = new DockObserver(context);
- } catch (Throwable e) {
- reportWtf("starting DockObserver", e);
- }
+ mSystemServiceManager.startService(DockObserver.class);
}
if (!disableMedia) {
@@ -948,6 +942,12 @@ public final class SystemServer {
}
try {
+ mSystemServiceManager.startService(RestrictionsManagerService.class);
+ } catch (Throwable e) {
+ reportWtf("starting RestrictionsManagerService", e);
+ }
+
+ try {
mSystemServiceManager.startService(MediaSessionService.class);
} catch (Throwable e) {
reportWtf("starting MediaSessionService", e);
@@ -998,8 +998,7 @@ public final class SystemServer {
try {
Slog.i(TAG, "LauncherAppsService");
- LauncherAppsService las = new LauncherAppsService(context);
- ServiceManager.addService(Context.LAUNCHER_APPS_SERVICE, las);
+ mSystemServiceManager.startService(LauncherAppsService.class);
} catch (Throwable t) {
reportWtf("starting LauncherAppsService", t);
}
@@ -1085,7 +1084,6 @@ public final class SystemServer {
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
final NetworkScoreService networkScoreF = networkScore;
- final DockObserver dockF = dock;
final WallpaperManagerService wallpaperF = wallpaper;
final InputMethodManagerService immF = imm;
final RecognitionManagerService recognitionF = recognition;
@@ -1159,11 +1157,6 @@ public final class SystemServer {
reportWtf("making Connectivity Service ready", e);
}
try {
- if (dockF != null) dockF.systemReady();
- } catch (Throwable e) {
- reportWtf("making Dock Service ready", e);
- }
- try {
if (recognitionF != null) recognitionF.systemReady();
} catch (Throwable e) {
reportWtf("making Recognition Service ready", e);
diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk
new file mode 100644
index 0000000..fcf8626
--- /dev/null
+++ b/services/restrictions/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.restrictions
+
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,java)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
new file mode 100644
index 0000000..e1f77b3
--- /dev/null
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -0,0 +1,171 @@
+/*
+ * 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.restrictions;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.admin.IDevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IRestrictionsManager;
+import android.content.RestrictionsManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IUserManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemService;
+
+/**
+ * SystemService wrapper for the RestrictionsManager implementation. Publishes the
+ * Context.RESTRICTIONS_SERVICE.
+ */
+
+public final class RestrictionsManagerService extends SystemService {
+ private final RestrictionsManagerImpl mRestrictionsManagerImpl;
+
+ public RestrictionsManagerService(Context context) {
+ super(context);
+ mRestrictionsManagerImpl = new RestrictionsManagerImpl(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.RESTRICTIONS_SERVICE, mRestrictionsManagerImpl);
+ }
+
+ class RestrictionsManagerImpl extends IRestrictionsManager.Stub {
+ private final Context mContext;
+ private final IUserManager mUm;
+ private final IDevicePolicyManager mDpm;
+
+ public RestrictionsManagerImpl(Context context) {
+ mContext = context;
+ mUm = (IUserManager) getBinderService(Context.USER_SERVICE);
+ mDpm = (IDevicePolicyManager) getBinderService(Context.DEVICE_POLICY_SERVICE);
+ }
+
+ @Override
+ public Bundle getApplicationRestrictions(String packageName) throws RemoteException {
+ return mUm.getApplicationRestrictions(packageName);
+ }
+
+ @Override
+ public boolean hasRestrictionsProvider() throws RemoteException {
+ int userHandle = UserHandle.getCallingUserId();
+ if (mDpm != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mDpm.getRestrictionsProvider(userHandle) != null;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void requestPermission(String packageName, String requestTemplate,
+ Bundle requestData) throws RemoteException {
+ int callingUid = Binder.getCallingUid();
+ int userHandle = UserHandle.getUserId(callingUid);
+ if (mDpm != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ComponentName restrictionsProvider =
+ mDpm.getRestrictionsProvider(userHandle);
+ // Check if there is a restrictions provider
+ if (restrictionsProvider == null) {
+ throw new IllegalStateException(
+ "Cannot request permission without a restrictions provider registered");
+ }
+ // Check that the packageName matches the caller.
+ enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
+ " match caller ");
+ // Prepare and broadcast the intent to the provider
+ Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
+ intent.setComponent(restrictionsProvider);
+ intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(RestrictionsManager.EXTRA_TEMPLATE_ID, requestTemplate);
+ intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private void enforceCallerMatchesPackage(int callingUid, String packageName,
+ String message) {
+ try {
+ String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid);
+ if (pkgs != null) {
+ if (!ArrayUtils.contains(pkgs, packageName)) {
+ throw new SecurityException(message + callingUid);
+ }
+ }
+ } catch (RemoteException re) {
+ // Shouldn't happen
+ }
+ }
+
+ @Override
+ public void notifyPermissionResponse(String packageName, Bundle response)
+ throws RemoteException {
+ // Check caller
+ int callingUid = Binder.getCallingUid();
+ int userHandle = UserHandle.getUserId(callingUid);
+ if (mDpm != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ComponentName permProvider = mDpm.getRestrictionsProvider(userHandle);
+ if (permProvider == null) {
+ throw new SecurityException("No restrictions provider registered for user");
+ }
+ enforceCallerMatchesPackage(callingUid, permProvider.getPackageName(),
+ "Restrictions provider does not match caller ");
+
+ // Post the response to target package
+ Intent responseIntent = new Intent(
+ RestrictionsManager.ACTION_PERMISSION_RESPONSE_RECEIVED);
+ responseIntent.setPackage(packageName);
+ responseIntent.putExtra(RestrictionsManager.EXTRA_RESPONSE_BUNDLE, response);
+ mContext.sendBroadcastAsUser(responseIntent, new UserHandle(userHandle));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 7848b1d..636dd4d 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -35,7 +35,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
-
+ <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+
<application>
<uses-library android:name="android.test.runner" />
@@ -53,6 +54,15 @@
</intent-filter>
</service>
+ <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
</application>
<instrumentation
diff --git a/services/tests/servicestests/res/xml/device_admin_sample.xml b/services/tests/servicestests/res/xml/device_admin_sample.xml
new file mode 100644
index 0000000..032debb
--- /dev/null
+++ b/services/tests/servicestests/res/xml/device_admin_sample.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-policies>
+ <limit-password />
+ <watch-login />
+ <reset-password />
+ <force-lock />
+ <wipe-data />
+ <expire-password />
+ <encrypted-storage />
+ <disable-camera />
+ <disable-keyguard-features />
+ </uses-policies>
+</device-admin>
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
new file mode 100644
index 0000000..8e8e4e6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests for application restrictions persisting via profile owner:
+ * make -j FrameworksServicesTests
+ * runtest --path frameworks/base/services/tests/servicestests/ \
+ * src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
+ */
+public class ApplicationRestrictionsTest extends AndroidTestCase {
+
+ static DevicePolicyManager sDpm;
+ static ComponentName sAdminReceiver;
+ private static final String RESTRICTED_APP = "com.example.restrictedApp";
+ static boolean sAddBack = false;
+
+ public static class AdminReceiver extends DeviceAdminReceiver {
+
+ @Override
+ public void onDisabled(Context context, Intent intent) {
+ if (sAddBack) {
+ sDpm.setActiveAdmin(sAdminReceiver, false);
+ sAddBack = false;
+ }
+
+ super.onDisabled(context, intent);
+ }
+ }
+
+ @Override
+ public void setUp() {
+ final Context context = getContext();
+ sAdminReceiver = new ComponentName(mContext.getPackageName(),
+ AdminReceiver.class.getName());
+ sDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0);
+ sDpm.setProfileOwner(context.getPackageName(), "Test", UserHandle.myUserId());
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 1);
+ // Remove the admin if already registered. It's async, so add it back
+ // when the admin gets a broadcast. Otherwise add it back right away.
+ if (sDpm.isAdminActive(sAdminReceiver)) {
+ sAddBack = true;
+ sDpm.removeActiveAdmin(sAdminReceiver);
+ } else {
+ sDpm.setActiveAdmin(sAdminReceiver, false);
+ }
+ }
+
+ @Override
+ public void tearDown() {
+ Settings.Secure.putInt(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0);
+ sDpm.removeActiveAdmin(sAdminReceiver);
+ Settings.Secure.putInt(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 1);
+ }
+
+ public void testSettingRestrictions() {
+ Bundle restrictions = new Bundle();
+ restrictions.putString("KEY_STRING", "Foo");
+ assertNotNull(sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP));
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+ Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertNotNull(returned);
+ assertEquals(returned.size(), 1);
+ assertEquals(returned.get("KEY_STRING"), "Foo");
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
+ returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertEquals(returned.size(), 0);
+ }
+
+ public void testRestrictionTypes() {
+ Bundle restrictions = new Bundle();
+ restrictions.putString("KEY_STRING", "Foo");
+ restrictions.putInt("KEY_INT", 7);
+ restrictions.putBoolean("KEY_BOOLEAN", true);
+ restrictions.putBoolean("KEY_BOOLEAN_2", false);
+ restrictions.putString("KEY_STRING_2", "Bar");
+ restrictions.putStringArray("KEY_STR_ARRAY", new String[] { "Foo", "Bar" });
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+ Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertTrue(returned.getBoolean("KEY_BOOLEAN"));
+ assertFalse(returned.getBoolean("KEY_BOOLEAN_2"));
+ assertFalse(returned.getBoolean("KEY_BOOLEAN_3"));
+ assertEquals(returned.getInt("KEY_INT"), 7);
+ assertTrue(returned.get("KEY_BOOLEAN") instanceof Boolean);
+ assertTrue(returned.get("KEY_INT") instanceof Integer);
+ assertEquals(returned.get("KEY_STRING"), "Foo");
+ assertEquals(returned.get("KEY_STRING_2"), "Bar");
+ assertTrue(returned.getStringArray("KEY_STR_ARRAY") instanceof String[]);
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
+ }
+
+ public void testTextEscaping() {
+ String fancyText = "<This contains XML/> <JSON> "
+ + "{ \"One\": { \"OneOne\": \"11\", \"OneTwo\": \"12\" }, \"Two\": \"2\" } <JSON/>";
+ Bundle restrictions = new Bundle();
+ restrictions.putString("KEY_FANCY_TEXT", fancyText);
+ sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+ Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+ assertEquals(returned.getString("KEY_FANCY_TEXT"), fancyText);
+ }
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
new file mode 100644
index 0000000..a6fdee9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.notification;
+
+import android.app.Notification;
+import android.os.Bundle;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannableString;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class ValidateNotificationPeopleTest extends AndroidTestCase {
+
+ @SmallTest
+ public void testNoExtra() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertNull("lack of extra should return null", result);
+ }
+
+ @SmallTest
+ public void testSingleString() throws Exception {
+ String[] expected = { "foobar" };
+ Bundle bundle = new Bundle();
+ bundle.putString(Notification.EXTRA_PEOPLE, expected[0]);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("string should be in result[0]", expected, result);
+ }
+
+ @SmallTest
+ public void testSingleCharArray() throws Exception {
+ String[] expected = { "foobar" };
+ Bundle bundle = new Bundle();
+ bundle.putCharArray(Notification.EXTRA_PEOPLE, expected[0].toCharArray());
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("char[] should be in result[0]", expected, result);
+ }
+
+ @SmallTest
+ public void testSingleCharSequence() throws Exception {
+ String[] expected = { "foobar" };
+ Bundle bundle = new Bundle();
+ bundle.putCharSequence(Notification.EXTRA_PEOPLE, new SpannableString(expected[0]));
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("charSequence should be in result[0]", expected, result);
+ }
+
+ @SmallTest
+ public void testStringArraySingle() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "foobar" };
+ bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("wrapped string should be in result[0]", expected, result);
+ }
+
+ @SmallTest
+ public void testStringArrayMultiple() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "foo", "bar", "baz" };
+ bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("testStringArrayMultiple", expected, result);
+ }
+
+ @SmallTest
+ public void testStringArrayNulls() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "foo", null, "baz" };
+ bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("testStringArrayNulls", expected, result);
+ }
+
+ @SmallTest
+ public void testCharSequenceArrayMultiple() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "foo", "bar", "baz" };
+ CharSequence[] charSeqArray = new CharSequence[expected.length];
+ for (int i = 0; i < expected.length; i++) {
+ charSeqArray[i] = new SpannableString(expected[i]);
+ }
+ bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("testCharSequenceArrayMultiple", expected, result);
+ }
+
+ @SmallTest
+ public void testMixedCharSequenceArrayList() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "foo", "bar", "baz" };
+ CharSequence[] charSeqArray = new CharSequence[expected.length];
+ for (int i = 0; i < expected.length; i++) {
+ if (i % 2 == 0) {
+ charSeqArray[i] = expected[i];
+ } else {
+ charSeqArray[i] = new SpannableString(expected[i]);
+ }
+ }
+ bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("testMixedCharSequenceArrayList", expected, result);
+ }
+
+ @SmallTest
+ public void testStringArrayList() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "foo", null, "baz" };
+ final ArrayList<String> stringArrayList = new ArrayList<String>(expected.length);
+ for (int i = 0; i < expected.length; i++) {
+ stringArrayList.add(expected[i]);
+ }
+ bundle.putStringArrayList(Notification.EXTRA_PEOPLE, stringArrayList);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("testStringArrayList", expected, result);
+ }
+
+ @SmallTest
+ public void testCharSequenceArrayList() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "foo", "bar", "baz" };
+ final ArrayList<CharSequence> stringArrayList =
+ new ArrayList<CharSequence>(expected.length);
+ for (int i = 0; i < expected.length; i++) {
+ stringArrayList.add(new SpannableString(expected[i]));
+ }
+ bundle.putCharSequenceArrayList(Notification.EXTRA_PEOPLE, stringArrayList);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("testCharSequenceArrayList", expected, result);
+ }
+
+ private void assertStringArrayEquals(String message, String[] expected, String[] result) {
+ String expectedString = Arrays.toString(expected);
+ String resultString = Arrays.toString(result);
+ assertEquals(message + ": arrays differ", expectedString, resultString);
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
index 0946c5a..cc5d004 100644
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
@@ -84,7 +84,6 @@ public class UsbDebuggingManager implements Runnable {
while (true) {
int count = inputStream.read(buffer);
if (count < 0) {
- Slog.e(TAG, "got " + count + " reading");
break;
}
@@ -100,9 +99,6 @@ public class UsbDebuggingManager implements Runnable {
break;
}
}
- } catch (IOException ex) {
- Slog.e(TAG, "Communication error: ", ex);
- throw ex;
} finally {
closeSocket();
}