diff options
Diffstat (limited to 'services')
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 <System Audio Mode Request> 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 <Give Audio Status> 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 <User Control Pressed> 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 <User Control Released> 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(); } |