diff options
Diffstat (limited to 'services')
40 files changed, 1318 insertions, 581 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 8b524dd..08c47dc 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -75,6 +75,12 @@ class AlarmManagerService extends SystemService { // warning message. The time duration is in milliseconds. private static final long LATE_ALARM_THRESHOLD = 10 * 1000; + // Minimum futurity of a new alarm + private static final long MIN_FUTURITY = 5 * 1000; // 5 seconds, in millis + + // Minimum alarm recurrence interval + private static final long MIN_INTERVAL = 60 * 1000; // one minute, in millis + private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP; private static final int RTC_MASK = 1 << RTC; private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; @@ -696,6 +702,15 @@ class AlarmManagerService extends SystemService { windowLength = AlarmManager.INTERVAL_HOUR; } + // Sanity check the recurrence interval. This will catch people who supply + // seconds when the API expects milliseconds. + if (interval > 0 && interval < MIN_INTERVAL) { + Slog.w(TAG, "Suspiciously short interval " + interval + + " millis; expanding to " + (int)(MIN_INTERVAL/1000) + + " seconds"); + interval = MIN_INTERVAL; + } + if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) { throw new IllegalArgumentException("Invalid alarm type " + type); } @@ -709,7 +724,11 @@ class AlarmManagerService extends SystemService { } final long nowElapsed = SystemClock.elapsedRealtime(); - final long triggerElapsed = convertToElapsed(triggerAtTime, type); + final long nominalTrigger = convertToElapsed(triggerAtTime, type); + // Try to prevent spamming by making sure we aren't firing alarms in the immediate future + final long minTrigger = nowElapsed + MIN_FUTURITY; + final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger; + final long maxElapsed; if (windowLength == AlarmManager.WINDOW_EXACT) { maxElapsed = triggerElapsed; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 5997680..fa6d349 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2598,7 +2598,7 @@ public class ConnectivityService extends IConnectivityManager.Stub String exclList = ""; String pacFileUrl = ""; if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) || - (proxyProperties.getPacFileUrl() != null))) { + !Uri.EMPTY.equals(proxyProperties.getPacFileUrl()))) { if (!proxyProperties.isValid()) { if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString()); @@ -2608,7 +2608,7 @@ public class ConnectivityService extends IConnectivityManager.Stub host = mGlobalProxy.getHost(); port = mGlobalProxy.getPort(); exclList = mGlobalProxy.getExclusionListAsString(); - if (proxyProperties.getPacFileUrl() != null) { + if (!Uri.EMPTY.equals(proxyProperties.getPacFileUrl())) { pacFileUrl = proxyProperties.getPacFileUrl().toString(); } } else { @@ -2670,7 +2670,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleApplyDefaultProxy(ProxyInfo proxy) { if (proxy != null && TextUtils.isEmpty(proxy.getHost()) - && (proxy.getPacFileUrl() == null)) { + && Uri.EMPTY.equals(proxy.getPacFileUrl())) { proxy = null; } synchronized (mProxyLock) { @@ -2686,7 +2686,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // global (to get the correct local port), and send a broadcast. // TODO: Switch PacManager to have its own message to send back rather than // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy. - if ((mGlobalProxy != null) && (proxy != null) && (proxy.getPacFileUrl() != null) + if ((mGlobalProxy != null) && (proxy != null) + && (!Uri.EMPTY.equals(proxy.getPacFileUrl())) && proxy.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) { mGlobalProxy = proxy; sendProxyBroadcast(mGlobalProxy); @@ -2813,9 +2814,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Prepare for a VPN application. This method is used by VpnDialogs - * and not available in ConnectivityManager. Permissions are checked - * in Vpn class. + * Prepare for a VPN application. This method is used by system-privileged apps. + * Permissions are checked in Vpn class. * @hide */ @Override @@ -2829,8 +2829,8 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Set whether the current VPN package has the ability to launch VPNs without - * user intervention. This method is used by system UIs and not available - * in ConnectivityManager. Permissions are checked in Vpn class. + * user intervention. This method is used by system-privileged apps. + * Permissions are checked in Vpn class. * @hide */ @Override @@ -3247,7 +3247,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Settings.Global.putInt(cr, Settings.Global.AIRPLANE_MODE_ON, enable ? 1 : 0); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); intent.putExtra("state", enable); - mContext.sendBroadcast(intent); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } finally { Binder.restoreCallingIdentity(ident); } @@ -3840,9 +3840,8 @@ public class ConnectivityService extends IConnectivityManager.Stub int notificationType) { if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) { Intent intent = new Intent(); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK, nri.request); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK_REQUEST, - networkAgent.network); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK, nri.request); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, networkAgent.network); sendIntent(nri.mPendingIntent, intent); } // else not handled @@ -3974,7 +3973,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // another higher scoring network by another call to rematchNetworkAndRequests() // and this other call also lingered newNetwork. private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, boolean nascent) { - if (!newNetwork.created) loge("ERROR: uncreated network being rematched."); + if (!newNetwork.created) return; if (nascent && !newNetwork.validated) loge("ERROR: nascent network not validated."); boolean keep = newNetwork.isVPN(); boolean isNewDefault = false; @@ -4273,7 +4272,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final int oldScore = nai.getCurrentScore(); nai.setCurrentScore(score); - if (nai.created) rematchAllNetworksAndRequests(nai, oldScore); + rematchAllNetworksAndRequests(nai, oldScore); sendUpdatedScoreToFactories(nai); } diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 29b04da..a44cb72 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -408,9 +408,14 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { if (!newline) out.append("\n"); } else { String text = dbe.getText(70); - boolean truncated = (text.length() == 70); - out.append(" ").append(text.trim().replace('\n', '/')); - if (truncated) out.append(" ..."); + out.append(" "); + if (text == null) { + out.append("[null]"); + } else { + boolean truncated = (text.length() == 70); + out.append(text.trim().replace('\n', '/')); + if (truncated) out.append(" ..."); + } out.append("\n"); } } catch (IOException e) { diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java index 9596b57..da50751 100644 --- a/services/core/java/com/android/server/MmsServiceBroker.java +++ b/services/core/java/com/android/server/MmsServiceBroker.java @@ -16,8 +16,6 @@ package com.android.server; -import com.android.internal.telephony.IMms; - import android.Manifest; import android.app.AppOpsManager; import android.app.PendingIntent; @@ -38,6 +36,8 @@ import android.os.SystemClock; import android.telephony.TelephonyManager; import android.util.Slog; +import com.android.internal.telephony.IMms; + /** * This class is a proxy for MmsService APIs. We need this because MmsService runs * in phone process and may crash anytime. This manages a connection to the actual @@ -118,7 +118,7 @@ public class MmsServiceBroker extends SystemService { } public void systemRunning() { - tryConnecting(); + Slog.i(TAG, "Delay connecting to MmsService until an API is called"); } private void tryConnecting() { @@ -206,7 +206,7 @@ public class MmsServiceBroker extends SystemService { * Throws a security exception unless the caller has carrier privilege. */ private void enforceCarrierPrivilege() { - String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); for (String pkg : packages) { if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { @@ -216,12 +216,21 @@ public class MmsServiceBroker extends SystemService { throw new SecurityException("No carrier privilege"); } + private String getCallingPackageName() { + final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + if (packages != null && packages.length > 0) { + return packages[0]; + } + return "unknown"; + } + // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service" private final class BinderService extends IMms.Stub { @Override public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) throws RemoteException { + Slog.d(TAG, "sendMessage() by " + callingPkg); mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message"); if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) != AppOpsManager.MODE_ALLOWED) { @@ -235,6 +244,7 @@ public class MmsServiceBroker extends SystemService { public void downloadMessage(int subId, String callingPkg, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) throws RemoteException { + Slog.d(TAG, "downloadMessage() by " + callingPkg); mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS, "Download MMS message"); if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(), @@ -260,6 +270,7 @@ public class MmsServiceBroker extends SystemService { @Override public Bundle getCarrierConfigValues(int subId) throws RemoteException { + Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName()); return getServiceGuarded().getCarrierConfigValues(subId); } diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index de90aa2..17edb53 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -41,6 +41,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; /** * Service for reading and writing blocks to a persistent partition. @@ -63,22 +66,16 @@ public class PersistentDataBlockService extends SystemService { private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; private static final int HEADER_SIZE = 8; // Magic number to mark block device as adhering to the format consumed by this service - private static final int PARTITION_TYPE_MARKER = 0x1990; + private static final int PARTITION_TYPE_MARKER = 0x19901873; // Limit to 100k as blocks larger than this might cause strain on Binder. - // TODO(anmorales): Consider splitting up too-large blocks in PersistentDataBlockManager private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100; + public static final int DIGEST_SIZE_BYTES = 32; private final Context mContext; private final String mDataBlockFile; private final Object mLock = new Object(); private int mAllowedUid = -1; - /* - * Separate lock for OEM unlock related operations as they can happen in parallel with regular - * block operations. - */ - private final Object mOemLock = new Object(); - private long mBlockDeviceSize; public PersistentDataBlockService(Context context) { @@ -89,7 +86,6 @@ public class PersistentDataBlockService extends SystemService { mAllowedUid = getAllowedUid(UserHandle.USER_OWNER); } - private int getAllowedUid(int userHandle) { String allowedPackage = mContext.getResources() .getString(R.string.config_persistentDataPackageName); @@ -106,6 +102,7 @@ public class PersistentDataBlockService extends SystemService { @Override public void onStart() { + enforceChecksumValidity(); publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); } @@ -128,6 +125,9 @@ public class PersistentDataBlockService extends SystemService { } private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException { + // skip over checksum + inputStream.skipBytes(DIGEST_SIZE_BYTES); + int totalDataSize; int blockId = inputStream.readInt(); if (blockId == PARTITION_TYPE_MARKER) { @@ -148,6 +148,143 @@ public class PersistentDataBlockService extends SystemService { return mBlockDeviceSize; } + private boolean enforceChecksumValidity() { + byte[] storedDigest = new byte[DIGEST_SIZE_BYTES]; + + synchronized (mLock) { + byte[] digest = computeDigestLocked(storedDigest); + if (digest == null || !Arrays.equals(storedDigest, digest)) { + Slog.i(TAG, "Formatting FRP partition..."); + formatPartitionLocked(); + return false; + } + } + + return true; + } + + private boolean computeAndWriteDigestLocked() { + byte[] digest = computeDigestLocked(null); + if (digest != null) { + DataOutputStream outputStream; + try { + outputStream = new DataOutputStream( + new FileOutputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Slog.e(TAG, "partition not available?", e); + return false; + } + + try { + outputStream.write(digest, 0, DIGEST_SIZE_BYTES); + outputStream.flush(); + } catch (IOException e) { + Slog.e(TAG, "failed to write block checksum", e); + return false; + } finally { + IoUtils.closeQuietly(outputStream); + } + return true; + } else { + return false; + } + } + + private byte[] computeDigestLocked(byte[] storedDigest) { + DataInputStream inputStream; + try { + inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Slog.e(TAG, "partition not available?", e); + return null; + } + + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + // won't ever happen -- every implementation is required to support SHA-256 + Slog.e(TAG, "SHA-256 not supported?", e); + IoUtils.closeQuietly(inputStream); + return null; + } + + try { + if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) { + inputStream.read(storedDigest); + } else { + inputStream.skipBytes(DIGEST_SIZE_BYTES); + } + + int read; + byte[] data = new byte[1024]; + md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest + while ((read = inputStream.read(data)) != -1) { + md.update(data, 0, read); + } + } catch (IOException e) { + Slog.e(TAG, "failed to read partition", e); + return null; + } finally { + IoUtils.closeQuietly(inputStream); + } + + return md.digest(); + } + + private void formatPartitionLocked() { + DataOutputStream outputStream; + try { + outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Slog.e(TAG, "partition not available?", e); + return; + } + + byte[] data = new byte[DIGEST_SIZE_BYTES]; + try { + outputStream.write(data, 0, DIGEST_SIZE_BYTES); + outputStream.writeInt(PARTITION_TYPE_MARKER); + outputStream.writeInt(0); // data size + outputStream.flush(); + } catch (IOException e) { + Slog.e(TAG, "failed to format block", e); + return; + } finally { + IoUtils.closeQuietly(outputStream); + } + + doSetOemUnlockEnabledLocked(false); + computeAndWriteDigestLocked(); + } + + private void doSetOemUnlockEnabledLocked(boolean enabled) { + FileOutputStream outputStream; + try { + outputStream = new FileOutputStream(new File(mDataBlockFile)); + } catch (FileNotFoundException e) { + Slog.e(TAG, "partition not available", e); + return; + } + + try { + FileChannel channel = outputStream.getChannel(); + + channel.position(getBlockDeviceSize() - 1); + + ByteBuffer data = ByteBuffer.allocate(1); + data.put(enabled ? (byte) 1 : (byte) 0); + data.flip(); + channel.write(data); + outputStream.flush(); + } catch (IOException e) { + Slog.e(TAG, "unable to access persistent partition", e); + return; + } finally { + IoUtils.closeQuietly(outputStream); + } + } + private native long nativeGetBlockDeviceSize(String path); private native int nativeWipe(String path); @@ -176,19 +313,23 @@ public class PersistentDataBlockService extends SystemService { headerAndData.putInt(data.length); headerAndData.put(data); - try { - synchronized (mLock) { - outputStream.write(headerAndData.array()); - return data.length; - } - } catch (IOException e) { - Slog.e(TAG, "failed writing to the persistent data block", e); - return -1; - } finally { + synchronized (mLock) { try { - outputStream.close(); + byte[] checksum = new byte[DIGEST_SIZE_BYTES]; + outputStream.write(checksum, 0, DIGEST_SIZE_BYTES); + outputStream.write(headerAndData.array()); + outputStream.flush(); } catch (IOException e) { - Slog.e(TAG, "failed closing output stream", e); + Slog.e(TAG, "failed writing to the persistent data block", e); + return -1; + } finally { + IoUtils.closeQuietly(outputStream); + } + + if (computeAndWriteDigestLocked()) { + return data.length; + } else { + return -1; } } } @@ -196,6 +337,9 @@ public class PersistentDataBlockService extends SystemService { @Override public byte[] read() { enforceUid(Binder.getCallingUid()); + if (!enforceChecksumValidity()) { + return new byte[0]; + } DataInputStream inputStream; try { @@ -256,30 +400,10 @@ public class PersistentDataBlockService extends SystemService { } enforceOemUnlockPermission(); enforceIsOwner(); - FileOutputStream outputStream; - try { - outputStream = new FileOutputStream(new File(mDataBlockFile)); - } catch (FileNotFoundException e) { - Slog.e(TAG, "parition not available", e); - return; - } - - try { - FileChannel channel = outputStream.getChannel(); - channel.position(getBlockDeviceSize() - 1); - - ByteBuffer data = ByteBuffer.allocate(1); - data.put(enabled ? (byte) 1 : (byte) 0); - data.flip(); - - synchronized (mOemLock) { - channel.write(data); - } - } catch (IOException e) { - Slog.e(TAG, "unable to access persistent partition", e); - } finally { - IoUtils.closeQuietly(outputStream); + synchronized (mLock) { + doSetOemUnlockEnabledLocked(enabled); + computeAndWriteDigestLocked(); } } @@ -295,8 +419,8 @@ public class PersistentDataBlockService extends SystemService { } try { - inputStream.skip(getBlockDeviceSize() - 1); - synchronized (mOemLock) { + synchronized (mLock) { + inputStream.skip(getBlockDeviceSize() - 1); return inputStream.readByte() != 0; } } catch (IOException e) { @@ -336,6 +460,5 @@ public class PersistentDataBlockService extends SystemService { long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1; return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE; } - }; } diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index f4fb519..92fbc1e 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -16,6 +16,7 @@ package com.android.server; +import android.app.ActivityManager; import android.content.pm.FeatureInfo; import android.os.*; import android.os.Process; @@ -177,6 +178,8 @@ public class SystemConfig { return; } + final boolean lowRam = ActivityManager.isLowRamDeviceStatic(); + try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(permReader); @@ -276,10 +279,17 @@ public class SystemConfig { } else if ("feature".equals(name)) { String fname = parser.getAttributeValue(null, "name"); + boolean allowed; + if (!lowRam) { + allowed = true; + } else { + String notLowRam = parser.getAttributeValue(null, "notLowRam"); + allowed = !"true".equals(notLowRam); + } if (fname == null) { Slog.w(TAG, "<feature> without name at " + parser.getPositionDescription()); - } else { + } else if (allowed) { //Log.i(TAG, "Got feature " + fname); FeatureInfo fi = new FeatureInfo(); fi.name = fname; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index d480f68..a2f4d56 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -77,7 +77,6 @@ import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.FgThread; - import com.google.android.collect.Lists; import com.google.android.collect.Sets; @@ -487,7 +486,7 @@ public class AccountManagerService for (Account sa : sharedAccounts) { if (ArrayUtils.contains(accounts, sa)) continue; // Account doesn't exist. Copy it now. - copyAccountToUser(sa, UserHandle.USER_OWNER, userId); + copyAccountToUser(null /*no response*/, sa, UserHandle.USER_OWNER, userId); } } @@ -673,16 +672,31 @@ public class AccountManagerService } } - private boolean copyAccountToUser(final Account account, int userFrom, int userTo) { + @Override + public void copyAccountToUser(final IAccountManagerResponse response, final Account account, + int userFrom, int userTo) { + enforceCrossUserPermission(UserHandle.USER_ALL, "Calling copyAccountToUser requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); final UserAccounts fromAccounts = getUserAccounts(userFrom); final UserAccounts toAccounts = getUserAccounts(userTo); if (fromAccounts == null || toAccounts == null) { - return false; + if (response != null) { + Bundle result = new Bundle(); + result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); + try { + response.onResult(result); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to report error back to the client." + e); + } + } + return; } + Slog.d(TAG, "Copying account " + account.name + + " from user " + userFrom + " to user " + userTo); long identityToken = clearCallingIdentity(); try { - new Session(fromAccounts, null, account.type, false, + new Session(fromAccounts, response, account.type, false, false /* stripAuthTokenFromResult */) { @Override protected String toDebugString(long now) { @@ -697,12 +711,10 @@ public class AccountManagerService @Override public void onResult(Bundle result) { - if (result != null) { - if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { - // Create a Session for the target user and pass in the bundle - completeCloningAccount(result, account, toAccounts); - } - return; + if (result != null + && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { + // Create a Session for the target user and pass in the bundle + completeCloningAccount(response, result, account, toAccounts); } else { super.onResult(result); } @@ -711,14 +723,13 @@ public class AccountManagerService } finally { restoreCallingIdentity(identityToken); } - return true; } - void completeCloningAccount(final Bundle result, final Account account, - final UserAccounts targetUser) { + private void completeCloningAccount(IAccountManagerResponse response, + final Bundle accountCredentials, final Account account, final UserAccounts targetUser) { long id = clearCallingIdentity(); try { - new Session(targetUser, null, account.type, false, + new Session(targetUser, response, account.type, false, false /* stripAuthTokenFromResult */) { @Override protected String toDebugString(long now) { @@ -731,10 +742,10 @@ public class AccountManagerService // Confirm that the owner's account still exists before this step. UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); synchronized (owner.cacheLock) { - Account[] ownerAccounts = getAccounts(UserHandle.USER_OWNER); - for (Account acc : ownerAccounts) { + for (Account acc : getAccounts(UserHandle.USER_OWNER)) { if (acc.equals(account)) { - mAuthenticator.addAccountFromCredentials(this, account, result); + mAuthenticator.addAccountFromCredentials( + this, account, accountCredentials); break; } } @@ -743,17 +754,10 @@ public class AccountManagerService @Override public void onResult(Bundle result) { - if (result != null) { - if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { - // TODO: Anything? - } else { - // TODO: Show error notification - // TODO: Should we remove the shadow account to avoid retries? - } - return; - } else { - super.onResult(result); - } + // TODO: Anything to do if if succedded? + // TODO: If it failed: Show error notification? Should we remove the shadow + // account to avoid retries? + super.onResult(result); } @Override @@ -1043,7 +1047,8 @@ public class AccountManagerService } @Override - public void removeAccount(IAccountManagerResponse response, Account account) { + public void removeAccount(IAccountManagerResponse response, Account account, + boolean expectActivityLaunch) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "removeAccount: " + account + ", response " + response @@ -1088,7 +1093,7 @@ public class AccountManagerService } try { - new RemoveAccountSession(accounts, response, account).bind(); + new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind(); } finally { restoreCallingIdentity(identityToken); } @@ -1096,7 +1101,7 @@ public class AccountManagerService @Override public void removeAccountAsUser(IAccountManagerResponse response, Account account, - int userId) { + boolean expectActivityLaunch, int userId) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "removeAccount: " + account + ", response " + response @@ -1145,7 +1150,30 @@ public class AccountManagerService } try { - new RemoveAccountSession(accounts, response, account).bind(); + new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind(); + } finally { + restoreCallingIdentity(identityToken); + } + } + + @Override + public boolean removeAccountExplicitly(Account account) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "removeAccountExplicitly: " + account + + ", caller's uid " + Binder.getCallingUid() + + ", pid " + Binder.getCallingPid()); + } + if (account == null) throw new IllegalArgumentException("account is null"); + checkAuthenticateAccountsPermission(account); + + UserAccounts accounts = getUserAccountsForCaller(); + int userId = Binder.getCallingUserHandle().getIdentifier(); + if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { + return false; + } + long identityToken = clearCallingIdentity(); + try { + return removeAccountInternal(accounts, account); } finally { restoreCallingIdentity(identityToken); } @@ -1154,8 +1182,8 @@ public class AccountManagerService private class RemoveAccountSession extends Session { final Account mAccount; public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, - Account account) { - super(accounts, response, account.type, false /* expectActivityLaunch */, + Account account, boolean expectActivityLaunch) { + super(accounts, response, account.type, expectActivityLaunch, true /* stripAuthTokenFromResult */); mAccount = account; } @@ -1203,10 +1231,12 @@ public class AccountManagerService removeAccountInternal(getUserAccountsForCaller(), account); } - private void removeAccountInternal(UserAccounts accounts, Account account) { + private boolean removeAccountInternal(UserAccounts accounts, Account account) { + int deleted; synchronized (accounts.cacheLock) { final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", + deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + + "=?", new String[]{account.name, account.type}); removeAccountFromCacheLocked(accounts, account); sendAccountsChangedBroadcast(accounts.userId); @@ -1226,6 +1256,7 @@ public class AccountManagerService Binder.restoreCallingIdentity(id); } } + return (deleted > 0); } @Override @@ -2714,7 +2745,7 @@ public class AccountManagerService break; case MESSAGE_COPY_SHARED_ACCOUNT: - copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2); + copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2); break; default: diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d0f5eed..f0fb9e6 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -61,7 +61,6 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; import com.android.internal.app.ProcessStats; -import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessCpuTracker; @@ -796,7 +795,11 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public int hashCode() { - return toString().hashCode(); + int hashCode = 1; + hashCode = 31 * hashCode + sourceUserId; + hashCode = 31 * hashCode + uri.hashCode(); + hashCode = 31 * hashCode + (prefix ? 1231 : 1237); + return hashCode; } @Override @@ -835,10 +838,12 @@ public final class ActivityManagerService extends ActivityManagerNative * indirect content-provider access. */ private class Identity { - public int pid; - public int uid; + public final IBinder token; + public final int pid; + public final int uid; - Identity(int _pid, int _uid) { + Identity(IBinder _token, int _pid, int _uid) { + token = _token; pid = _pid; uid = _uid; } @@ -1183,7 +1188,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int SERVICE_TIMEOUT_MSG = 12; static final int UPDATE_TIME_ZONE = 13; static final int SHOW_UID_ERROR_MSG = 14; - static final int IM_FEELING_LUCKY_MSG = 15; + static final int SHOW_FINGERPRINT_ERROR_MSG = 15; static final int PROC_START_TIMEOUT_MSG = 20; static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21; static final int KILL_APPLICATION_MSG = 22; @@ -1212,13 +1217,13 @@ public final class ActivityManagerService extends ActivityManagerNative static final int FINISH_BOOTING_MSG = 45; static final int START_USER_SWITCH_MSG = 46; static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47; + static final int DISMISS_DIALOG_MSG = 48; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; static final int FIRST_COMPAT_MODE_MSG = 300; static final int FIRST_SUPERVISOR_STACK_MSG = 100; - AlertDialog mUidAlert; CompatModeDialog mCompatModeDialog; long mLastMemUsageReportTime = 0; @@ -1447,27 +1452,27 @@ public final class ActivityManagerService extends ActivityManagerNative } } break; case SHOW_UID_ERROR_MSG: { - String title = "System UIDs Inconsistent"; - String text = "UIDs on the system are inconsistent, you need to wipe your" - + " data partition or your device will be unstable."; - Log.e(TAG, title + ": " + text); if (mShowDialogs) { - // XXX This is a temporary dialog, no need to localize. AlertDialog d = new BaseErrorDialog(mContext); d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); d.setCancelable(false); - d.setTitle(title); - d.setMessage(text); - d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky", - mHandler.obtainMessage(IM_FEELING_LUCKY_MSG)); - mUidAlert = d; + d.setTitle(mContext.getText(R.string.android_system_label)); + d.setMessage(mContext.getText(R.string.system_error_wipe_data)); + d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok), + mHandler.obtainMessage(DISMISS_DIALOG_MSG, d)); d.show(); } } break; - case IM_FEELING_LUCKY_MSG: { - if (mUidAlert != null) { - mUidAlert.dismiss(); - mUidAlert = null; + case SHOW_FINGERPRINT_ERROR_MSG: { + if (mShowDialogs) { + AlertDialog d = new BaseErrorDialog(mContext); + d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); + d.setCancelable(false); + d.setTitle(mContext.getText(R.string.android_system_label)); + d.setMessage(mContext.getText(R.string.system_error_manufacturer)); + d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok), + mHandler.obtainMessage(DISMISS_DIALOG_MSG, d)); + d.show(); } } break; case PROC_START_TIMEOUT_MSG: { @@ -1727,6 +1732,11 @@ public final class ActivityManagerService extends ActivityManagerNative } break; } + case DISMISS_DIALOG_MSG: { + final Dialog d = (Dialog) msg.obj; + d.dismiss(); + break; + } } } }; @@ -1776,7 +1786,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } - int i=0, num=0; + int i = 0; + int num = 0; long[] tmp = new long[1]; do { ProcessRecord proc; @@ -1826,99 +1837,6 @@ public final class ActivityManagerService extends ActivityManagerNative } }; - /** - * Monitor for package changes and update our internal state. - */ - private final PackageMonitor mPackageMonitor = new PackageMonitor() { - @Override - public void onPackageRemoved(String packageName, int uid) { - // Remove all tasks with activities in the specified package from the list of recent tasks - final int eventUserId = getChangingUserId(); - synchronized (ActivityManagerService.this) { - for (int i = mRecentTasks.size() - 1; i >= 0; i--) { - TaskRecord tr = mRecentTasks.get(i); - if (tr.userId != eventUserId) continue; - - ComponentName cn = tr.intent.getComponent(); - if (cn != null && cn.getPackageName().equals(packageName)) { - // If the package name matches, remove the task - removeTaskByIdLocked(tr.taskId, true); - } - } - } - } - - @Override - public boolean onPackageChanged(String packageName, int uid, String[] components) { - onPackageModified(packageName); - return true; - } - - @Override - public void onPackageModified(String packageName) { - final int eventUserId = getChangingUserId(); - final IPackageManager pm = AppGlobals.getPackageManager(); - final ArrayList<Pair<Intent, Integer>> recentTaskIntents = - new ArrayList<Pair<Intent, Integer>>(); - final HashSet<ComponentName> componentsKnownToExist = new HashSet<ComponentName>(); - final ArrayList<Integer> tasksToRemove = new ArrayList<Integer>(); - // Copy the list of recent tasks so that we don't hold onto the lock on - // ActivityManagerService for long periods while checking if components exist. - synchronized (ActivityManagerService.this) { - for (int i = mRecentTasks.size() - 1; i >= 0; i--) { - TaskRecord tr = mRecentTasks.get(i); - if (tr.userId != eventUserId) continue; - - recentTaskIntents.add(new Pair<Intent, Integer>(tr.intent, tr.taskId)); - } - } - // Check the recent tasks and filter out all tasks with components that no longer exist. - for (int i = recentTaskIntents.size() - 1; i >= 0; i--) { - Pair<Intent, Integer> p = recentTaskIntents.get(i); - ComponentName cn = p.first.getComponent(); - if (cn != null && cn.getPackageName().equals(packageName)) { - if (componentsKnownToExist.contains(cn)) { - // If we know that the component still exists in the package, then skip - continue; - } - try { - ActivityInfo info = pm.getActivityInfo(cn, 0, eventUserId); - if (info != null) { - componentsKnownToExist.add(cn); - } else { - tasksToRemove.add(p.second); - } - } catch (RemoteException e) { - Log.e(TAG, "Failed to query activity info for component: " + cn, e); - } - } - } - // Prune all the tasks with removed components from the list of recent tasks - synchronized (ActivityManagerService.this) { - for (int i = tasksToRemove.size() - 1; i >= 0; i--) { - removeTaskByIdLocked(tasksToRemove.get(i), false); - } - } - } - - @Override - public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { - // Force stop the specified packages - int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - if (packages != null) { - for (String pkg : packages) { - synchronized (ActivityManagerService.this) { - if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, - userId, "finished booting")) { - return true; - } - } - } - } - return false; - } - }; - public void setSystemProcess() { try { ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true); @@ -2359,15 +2277,19 @@ public final class ActivityManagerService extends ActivityManagerNative * process when the bindApplication() IPC is sent to the process. They're * lazily setup to make sure the services are running when they're asked for. */ - private HashMap<String, IBinder> getCommonServicesLocked() { + private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) { if (mAppBindArgs == null) { - mAppBindArgs = new HashMap<String, IBinder>(); + mAppBindArgs = new HashMap<>(); - // Setup the application init args - mAppBindArgs.put("package", ServiceManager.getService("package")); - mAppBindArgs.put("window", ServiceManager.getService("window")); - mAppBindArgs.put(Context.ALARM_SERVICE, - ServiceManager.getService(Context.ALARM_SERVICE)); + // Isolated processes won't get this optimization, so that we don't + // violate the rules about which services they have access to. + if (!isolated) { + // Setup the application init args + mAppBindArgs.put("package", ServiceManager.getService("package")); + mAppBindArgs.put("window", ServiceManager.getService("window")); + mAppBindArgs.put(Context.ALARM_SERVICE, + ServiceManager.getService(Context.ALARM_SERVICE)); + } } return mAppBindArgs; } @@ -5990,7 +5912,8 @@ public final class ActivityManagerService extends ActivityManagerNative profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent, - new Configuration(mConfiguration), app.compat, getCommonServicesLocked(), + new Configuration(mConfiguration), app.compat, + getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked()); updateLruProcessLocked(app, false, null); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); @@ -6166,8 +6089,26 @@ public final class ActivityManagerService extends ActivityManagerNative } } - // Register receivers to handle package update events - mPackageMonitor.register(mContext, Looper.getMainLooper(), UserHandle.ALL, false); + IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + pkgFilter.addDataScheme("package"); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + if (pkgs != null) { + for (String pkg : pkgs) { + synchronized (ActivityManagerService.this) { + if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, + 0, "finished booting")) { + setResultCode(Activity.RESULT_OK); + return; + } + } + } + } + } + }, pkgFilter); // Let system services know. mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED); @@ -6326,7 +6267,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { - stack.backgroundResourcesReleased(token); + stack.backgroundResourcesReleased(); } } } finally { @@ -6792,21 +6733,9 @@ public final class ActivityManagerService extends ActivityManagerNative */ int checkComponentPermission(String permission, int pid, int uid, int owningUid, boolean exported) { - // We might be performing an operation on behalf of an indirect binder - // invocation, e.g. via {@link #openContentUri}. Check and adjust the - // client identity accordingly before proceeding. - Identity tlsIdentity = sCallerIdentity.get(); - if (tlsIdentity != null) { - Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {" - + tlsIdentity.pid + "," + tlsIdentity.uid + "}"); - uid = tlsIdentity.uid; - pid = tlsIdentity.pid; - } - if (pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; } - return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported); } @@ -6828,6 +6757,26 @@ public final class ActivityManagerService extends ActivityManagerNative return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true); } + @Override + public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken) { + if (permission == null) { + return PackageManager.PERMISSION_DENIED; + } + + // We might be performing an operation on behalf of an indirect binder + // invocation, e.g. via {@link #openContentUri}. Check and adjust the + // client identity accordingly before proceeding. + Identity tlsIdentity = sCallerIdentity.get(); + if (tlsIdentity != null && tlsIdentity.token == callerToken) { + Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {" + + tlsIdentity.pid + "," + tlsIdentity.uid + "}"); + uid = tlsIdentity.uid; + pid = tlsIdentity.pid; + } + + return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true); + } + /** * Binder IPC calls go through the public entry point. * This can be called with or without the global lock held. @@ -7033,13 +6982,13 @@ public final class ActivityManagerService extends ActivityManagerNative */ @Override public int checkUriPermission(Uri uri, int pid, int uid, - final int modeFlags, int userId) { + final int modeFlags, int userId, IBinder callerToken) { enforceNotIsolatedCaller("checkUriPermission"); // Another redirected-binder-call permissions check as in - // {@link checkComponentPermission}. + // {@link checkPermissionWithToken}. Identity tlsIdentity = sCallerIdentity.get(); - if (tlsIdentity != null) { + if (tlsIdentity != null && tlsIdentity.token == callerToken) { uid = tlsIdentity.uid; pid = tlsIdentity.pid; } @@ -8401,6 +8350,47 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private void removeTasksByPackageNameLocked(String packageName, int userId) { + // Remove all tasks with activities in the specified package from the list of recent tasks + for (int i = mRecentTasks.size() - 1; i >= 0; i--) { + TaskRecord tr = mRecentTasks.get(i); + if (tr.userId != userId) continue; + + ComponentName cn = tr.intent.getComponent(); + if (cn != null && cn.getPackageName().equals(packageName)) { + // If the package name matches, remove the task. + removeTaskByIdLocked(tr.taskId, true); + } + } + } + + private void removeTasksByRemovedPackageComponentsLocked(String packageName, int userId) { + final IPackageManager pm = AppGlobals.getPackageManager(); + final HashSet<ComponentName> componentsKnownToExist = new HashSet<ComponentName>(); + + for (int i = mRecentTasks.size() - 1; i >= 0; i--) { + TaskRecord tr = mRecentTasks.get(i); + if (tr.userId != userId) continue; + + ComponentName cn = tr.intent.getComponent(); + if (cn != null && cn.getPackageName().equals(packageName)) { + // Skip if component still exists in the package. + if (componentsKnownToExist.contains(cn)) continue; + + try { + ActivityInfo info = pm.getActivityInfo(cn, 0, userId); + if (info != null) { + componentsKnownToExist.add(cn); + } else { + removeTaskByIdLocked(tr.taskId, false); + } + } catch (RemoteException e) { + Log.e(TAG, "Activity info query failed. component=" + cn, e); + } + } + } + } + /** * Removes the task with the specified task id. * @@ -8460,6 +8450,7 @@ public final class ActivityManagerService extends ActivityManagerNative try { final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); if (task == null) { + Slog.d(TAG, "Could not find task for id: "+ taskId); return; } if (mStackSupervisor.isLockTaskModeViolation(task)) { @@ -9904,10 +9895,11 @@ public final class ActivityManagerService extends ActivityManagerNative // we do the check against the caller's permissions even though it looks // to the content provider like the Activity Manager itself is making // the request. + Binder token = new Binder(); sCallerIdentity.set(new Identity( - Binder.getCallingPid(), Binder.getCallingUid())); + token, Binder.getCallingPid(), Binder.getCallingUid())); try { - pfd = cph.provider.openFile(null, uri, "r", null); + pfd = cph.provider.openFile(null, uri, "r", null, token); } catch (FileNotFoundException e) { // do nothing; pfd will be returned null } finally { @@ -11249,13 +11241,18 @@ public final class ActivityManagerService extends ActivityManagerNative try { if (AppGlobals.getPackageManager().hasSystemUidErrors()) { - Message msg = Message.obtain(); - msg.what = SHOW_UID_ERROR_MSG; - mHandler.sendMessage(msg); + Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your" + + " data partition or your device will be unstable."); + mHandler.obtainMessage(SHOW_UID_ERROR_MSG).sendToTarget(); } } catch (RemoteException e) { } + if (!Build.isFingerprintConsistent()) { + Slog.e(TAG, "Build fingerprint is not consistent, warning user"); + mHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_MSG).sendToTarget(); + } + long ident = Binder.clearCallingIdentity(); try { Intent intent = new Intent(Intent.ACTION_USER_STARTED); @@ -13961,7 +13958,9 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<MemItem> procMems = new ArrayList<MemItem>(); final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>(); - long nativePss=0, dalvikPss=0, otherPss=0; + long nativePss = 0; + long dalvikPss = 0; + long otherPss = 0; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; @@ -15660,128 +15659,135 @@ public final class ActivityManagerService extends ActivityManagerNative } } - // Handle special intents: if this broadcast is from the package - // manager about a package being removed, we need to remove all of - // its activities from the history stack. - final boolean uidRemoved = Intent.ACTION_UID_REMOVED.equals( - intent.getAction()); - if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) - || Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction()) - || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction()) - || Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction()) - || uidRemoved) { - if (checkComponentPermission( - android.Manifest.permission.BROADCAST_PACKAGE_REMOVED, - callingPid, callingUid, -1, true) - == PackageManager.PERMISSION_GRANTED) { - if (uidRemoved) { - final Bundle intentExtras = intent.getExtras(); - final int uid = intentExtras != null - ? intentExtras.getInt(Intent.EXTRA_UID) : -1; - if (uid >= 0) { - BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics(); - synchronized (bs) { - bs.removeUidStatsLocked(uid); - } - mAppOpsService.uidRemoved(uid); + final String action = intent.getAction(); + if (action != null) { + switch (action) { + case Intent.ACTION_UID_REMOVED: + case Intent.ACTION_PACKAGE_REMOVED: + case Intent.ACTION_PACKAGE_CHANGED: + case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: + case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: + // Handle special intents: if this broadcast is from the package + // manager about a package being removed, we need to remove all of + // its activities from the history stack. + if (checkComponentPermission( + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED, + callingPid, callingUid, -1, true) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + " (pid=" + callingPid + + ", uid=" + callingUid + ")" + + " requires " + + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED; + Slog.w(TAG, msg); + throw new SecurityException(msg); } - } else { - // If resources are unavailable just force stop all - // those packages and flush the attribute cache as well. - if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) { - String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - if (list != null && (list.length > 0)) { - for (String pkg : list) { - forceStopPackageLocked(pkg, -1, false, true, true, false, false, userId, - "storage unmount"); + switch (action) { + case Intent.ACTION_UID_REMOVED: + final Bundle intentExtras = intent.getExtras(); + final int uid = intentExtras != null + ? intentExtras.getInt(Intent.EXTRA_UID) : -1; + if (uid >= 0) { + BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics(); + synchronized (bs) { + bs.removeUidStatsLocked(uid); + } + mAppOpsService.uidRemoved(uid); } - cleanupRecentTasksLocked(UserHandle.USER_ALL); - sendPackageBroadcastLocked( - IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, userId); - } - } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals( - intent.getAction())) { - cleanupRecentTasksLocked(UserHandle.USER_ALL); - } else { - Uri data = intent.getData(); - String ssp; - if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { - boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals( - intent.getAction()); - boolean fullUninstall = removed && - !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) { - forceStopPackageLocked(ssp, UserHandle.getAppId( - intent.getIntExtra(Intent.EXTRA_UID, -1)), false, true, true, - false, fullUninstall, userId, - removed ? "pkg removed" : "pkg changed"); + break; + case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: + // If resources are unavailable just force stop all those packages + // and flush the attribute cache as well. + String list[] = + intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + if (list != null && list.length > 0) { + for (int i = 0; i < list.length; i++) { + forceStopPackageLocked(list[i], -1, false, true, true, + false, false, userId, "storage unmount"); + } + cleanupRecentTasksLocked(UserHandle.USER_ALL); + sendPackageBroadcastLocked( + IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, + userId); } - if (removed) { - sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED, - new String[] {ssp}, userId); - if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - mAppOpsService.packageRemoved( - intent.getIntExtra(Intent.EXTRA_UID, -1), ssp); - - // Remove all permissions granted from/to this package - removeUriPermissionsForPackageLocked(ssp, userId, true); + break; + case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: + cleanupRecentTasksLocked(UserHandle.USER_ALL); + break; + case Intent.ACTION_PACKAGE_REMOVED: + case Intent.ACTION_PACKAGE_CHANGED: + Uri data = intent.getData(); + String ssp; + if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { + boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action); + boolean fullUninstall = removed && + !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) { + forceStopPackageLocked(ssp, UserHandle.getAppId( + intent.getIntExtra(Intent.EXTRA_UID, -1)), + false, true, true, false, fullUninstall, userId, + removed ? "pkg removed" : "pkg changed"); + } + if (removed) { + sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED, + new String[] {ssp}, userId); + if (fullUninstall) { + mAppOpsService.packageRemoved( + intent.getIntExtra(Intent.EXTRA_UID, -1), ssp); + + // Remove all permissions granted from/to this package + removeUriPermissionsForPackageLocked(ssp, userId, true); + + removeTasksByPackageNameLocked(ssp, userId); + } + } else { + removeTasksByRemovedPackageComponentsLocked(ssp, userId); } } + break; + } + break; + case Intent.ACTION_PACKAGE_ADDED: + // Special case for adding a package: by default turn on compatibility mode. + Uri data = intent.getData(); + String ssp; + if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { + final boolean replacing = + intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + mCompatModePackages.handlePackageAddedLocked(ssp, replacing); + + if (replacing) { + removeTasksByRemovedPackageComponentsLocked(ssp, userId); } } - } - } else { - String msg = "Permission Denial: " + intent.getAction() - + " broadcast from " + callerPackage + " (pid=" + callingPid - + ", uid=" + callingUid + ")" - + " requires " - + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - - // Special case for adding a package: by default turn on compatibility - // mode. - } else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) { - Uri data = intent.getData(); - String ssp; - if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { - mCompatModePackages.handlePackageAddedLocked(ssp, - intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)); - } - } - - /* - * If this is the time zone changed action, queue up a message that will reset the timezone - * of all currently running processes. This message will get queued up before the broadcast - * happens. - */ - if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { - mHandler.sendEmptyMessage(UPDATE_TIME_ZONE); - } - - /* - * If the user set the time, let all running processes know. - */ - if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { - final int is24Hour = intent.getBooleanExtra( - Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1 : 0; - mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0)); - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - stats.noteCurrentTimeChangedLocked(); + break; + case Intent.ACTION_TIMEZONE_CHANGED: + // If this is the time zone changed action, queue up a message that will reset + // the timezone of all currently running processes. This message will get + // queued up before the broadcast happens. + mHandler.sendEmptyMessage(UPDATE_TIME_ZONE); + break; + case Intent.ACTION_TIME_CHANGED: + // If the user set the time, let all running processes know. + final int is24Hour = + intent.getBooleanExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1 + : 0; + mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0)); + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + stats.noteCurrentTimeChangedLocked(); + } + break; + case Intent.ACTION_CLEAR_DNS_CACHE: + mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG); + break; + case Proxy.PROXY_CHANGE_ACTION: + ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO); + mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy)); + break; } } - if (Intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) { - mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG); - } - - if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) { - ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO); - mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy)); - } - // Add to the sticky list if requested. if (sticky) { if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 3a1fafe..c8b7205 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3317,17 +3317,18 @@ final class ActivityStack { mHandler.sendEmptyMessageDelayed(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG, 500); } else { Slog.e(TAG, "releaseBackgroundResources: activity " + r + " no longer running"); - backgroundResourcesReleased(r.appToken); + backgroundResourcesReleased(); } } } - final void backgroundResourcesReleased(IBinder token) { + final void backgroundResourcesReleased() { mHandler.removeMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG); final ActivityRecord r = getVisibleBehindActivity(); if (r != null) { mStackSupervisor.mStoppingActivities.add(r); setVisibleBehindActivity(null); + mStackSupervisor.scheduleIdleTimeoutLocked(null); } mStackSupervisor.resumeTopActivitiesLocked(); } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 120002e..38809cb 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -121,7 +121,7 @@ public final class ActivityStackSupervisor implements DisplayListener { static final boolean DEBUG_RELEASE = DEBUG || false; static final boolean DEBUG_SAVED_STATE = DEBUG || false; static final boolean DEBUG_SCREENSHOTS = DEBUG || false; - static final boolean DEBUG_STATES = DEBUG || true; + static final boolean DEBUG_STATES = DEBUG || false; static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false; public static final int HOME_STACK_ID = 0; diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index b331c84..9311f25 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -63,6 +63,12 @@ public class TaskPersister { private static final String IMAGES_DIRNAME = "recent_images"; static final String IMAGE_EXTENSION = ".png"; + // Directory where restored historical task XML/PNG files are placed. This directory + // contains subdirs named after TASKS_DIRNAME and IMAGES_DIRNAME mirroring the + // ancestral device's dataset. This needs to match the RECENTS_TASK_RESTORE_DIR + // value in RecentsBackupHelper. + private static final String RESTORED_TASKS = "restored_" + TASKS_DIRNAME; + private static final String TAG_TASK = "task"; static File sImagesDir; diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 576556b..3fa21d0 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -63,8 +63,8 @@ public class Nat464Xlat extends BaseNetworkObserver { // - Idle: start() not called. Everything is null. // - Starting: start() called. Interfaces are non-null. isStarted() returns true. // mIsRunning is false. - // - Running: start() called, and interfaceAdded() told us that mIface is up. Clat IP address - // is non-null. mIsRunning is true. + // - Running: start() called, and interfaceLinkStateChanged() told us that mIface is up. + // mIsRunning is true. // // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on // its handler thread must not modify any internal state variables; they are only updated by the @@ -236,10 +236,10 @@ public class Nat464Xlat extends BaseNetworkObserver { } @Override - public void interfaceAdded(String iface) { + public void interfaceLinkStateChanged(String iface, boolean up) { // Called by the InterfaceObserver on its own thread, so can race with stop(). - if (isStarted() && mIface.equals(iface)) { - Slog.i(TAG, "interface " + iface + " added, mIsRunning " + mIsRunning + "->true"); + if (isStarted() && up && mIface.equals(iface)) { + Slog.i(TAG, "interface " + iface + " is up, mIsRunning " + mIsRunning + "->true"); if (!mIsRunning) { LinkAddress clatAddress = getLinkAddress(iface); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 83756aa..03c05ec 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -23,6 +23,7 @@ import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; +import android.Manifest; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.PendingIntent; @@ -739,31 +740,7 @@ public class Vpn { }; private void enforceControlPermission() { - // System user is allowed to control VPN. - if (Binder.getCallingUid() == Process.SYSTEM_UID) { - return; - } - int appId = UserHandle.getAppId(Binder.getCallingUid()); - final long token = Binder.clearCallingIdentity(); - try { - // System VPN dialogs are also allowed to control VPN. - PackageManager pm = mContext.getPackageManager(); - ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0); - if (((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == app.uid)) { - return; - } - // SystemUI dialogs are also allowed to control VPN. - ApplicationInfo sysUiApp = pm.getApplicationInfo("com.android.systemui", 0); - if (((sysUiApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == sysUiApp.uid)) { - return; - } - } catch (Exception e) { - // ignore - } finally { - Binder.restoreCallingIdentity(token); - } - - throw new SecurityException("Unauthorized Caller"); + mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller"); } private class Connection implements ServiceConnection { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 81cd94b..78610ff 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -582,11 +582,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call state = mPowerState.getScreenState(); // Use zero brightness when screen is off. - // Use full brightness when screen brightness is boosted. if (state == Display.STATE_OFF) { brightness = PowerManager.BRIGHTNESS_OFF; - } else if (mPowerRequest.boostScreenBrightness) { - brightness = PowerManager.BRIGHTNESS_ON; } // Configure auto-brightness. @@ -601,6 +598,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON); } + // Apply brightness boost. + // We do this here after configuring auto-brightness so that we don't + // disable the light sensor during this temporary state. That way when + // boost ends we will be able to resume normal auto-brightness behavior + // without any delay. + if (mPowerRequest.boostScreenBrightness + && brightness != PowerManager.BRIGHTNESS_OFF) { + brightness = PowerManager.BRIGHTNESS_ON; + } + // Apply auto-brightness. boolean slowChange = false; if (brightness < 0) { @@ -662,11 +669,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Animate the screen brightness when the screen is on or dozing. // Skip the animation when the screen is off or suspended. - if (state == Display.STATE_ON || state == Display.STATE_DOZE) { - animateScreenBrightness(brightness, - slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST); - } else { - animateScreenBrightness(brightness, 0); + if (!mPendingScreenOff) { + if (state == Display.STATE_ON || state == Display.STATE_DOZE) { + animateScreenBrightness(brightness, + slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST); + } else { + animateScreenBrightness(brightness, 0); + } } // Determine whether the display is ready for use in the newly requested state. diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 0232bad..28d5fc0 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -23,6 +23,7 @@ import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.os.Handler; import android.os.IBinder; +import android.os.SystemProperties; import android.os.IBinder.DeathRecipient; import android.os.Message; import android.os.RemoteException; @@ -278,6 +279,15 @@ final class VirtualDisplayAdapter extends DisplayAdapter { } if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) { mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; + + if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { + // For demonstration purposes, allow rotation of the external display. + // In the future we might allow the user to configure this directly. + if ("portrait".equals(SystemProperties.get( + "persist.demo.remoterotation"))) { + mInfo.rotation = Surface.ROTATION_270; + } + } } mInfo.type = Display.TYPE_VIRTUAL; mInfo.touch = DisplayDeviceInfo.TOUCH_NONE; diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java new file mode 100644 index 0000000..68311de --- /dev/null +++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java @@ -0,0 +1,133 @@ +/* + * 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.HdmiDeviceInfo; +import android.util.Slog; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * Buffer storage to keep incoming messages for later processing. Used to + * handle messages that arrive when the device is not ready. Useful when + * keeping the messages from a connected device which are not discovered yet. + */ +final class DelayedMessageBuffer { + private final ArrayList<HdmiCecMessage> mBuffer = new ArrayList<>(); + private final HdmiCecLocalDevice mDevice; + + DelayedMessageBuffer(HdmiCecLocalDevice device) { + mDevice = device; + } + + /** + * Add a new message to the buffer. The buffer keeps selected messages in + * the order they are received. + * + * @param message {@link HdmiCecMessage} to add + */ + void add(HdmiCecMessage message) { + boolean buffered = true; + + // Note that all the messages are not handled in the same manner. + // For <Active Source> we keep the latest one only. + // TODO: This might not be the best way to choose the active source. + // Devise a better way to pick up the best one. + switch (message.getOpcode()) { + case Constants.MESSAGE_ACTIVE_SOURCE: + removeActiveSource(); + mBuffer.add(message); + break; + case Constants.MESSAGE_INITIATE_ARC: + mBuffer.add(message); + break; + default: + buffered = false; + break; + } + if (buffered) { + HdmiLogger.debug("Buffering message:" + message); + } + } + + private void removeActiveSource() { + // Uses iterator to remove elements while looping through the list. + for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { + HdmiCecMessage message = iter.next(); + if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) { + iter.remove(); + } + } + } + + void processAllMessages() { + for (HdmiCecMessage message : mBuffer) { + mDevice.onMessage(message); + HdmiLogger.debug("Processing message:" + message); + } + mBuffer.clear(); + } + + /** + * Process messages from a given logical device. Called by + * {@link NewDeviceAction} actions when they finish adding the device + * information. + * <p><Active Source> is not processed in this method but processed + * separately via {@link #processActiveSource()}. + * + * @param address logical address of CEC device which the messages to process + * are associated with + */ + void processMessagesForDevice(int address) { + HdmiLogger.debug("Processing message for address:" + address); + for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { + HdmiCecMessage message = iter.next(); + if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) { + continue; + } + if (message.getSource() == address) { + mDevice.onMessage(message); + HdmiLogger.debug("Processing message:" + message); + iter.remove(); + } + } + } + + /** + * Process <Active Source>. + * + * <p>The message has a dependency on TV input framework. Should be invoked + * after we get the callback + * {@link android.media.tv.TvInputManager.TvInputCallback#onInputAdded(String)} + * to ensure the processing of the message takes effect when transformed + * to input change callback. + * + * @param address logical address of the device to be the active source + */ + void processActiveSource(int address) { + for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { + HdmiCecMessage message = iter.next(); + if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE + && message.getSource() == address) { + mDevice.onMessage(message); + HdmiLogger.debug("Processing message:" + message); + iter.remove(); + } + } + } +} diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index 2ec9778..da404c4 100644 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java @@ -326,6 +326,8 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { Slog.v(TAG, "--------------------------------------------"); mCallback.onDeviceDiscoveryDone(result); finish(); + // Process any commands buffered while device discovery action was in progress. + tv().processAllDelayedMessages(); } private void checkAndProceedStage() { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 6536165..ec38124 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -30,6 +30,8 @@ import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANAL import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; +import android.annotation.Nullable; +import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiRecordSources; @@ -37,6 +39,9 @@ import android.hardware.hdmi.HdmiTimerRecordSources; import android.hardware.hdmi.IHdmiControlCallback; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.tv.TvInputInfo; +import android.media.tv.TvInputManager; +import android.media.tv.TvInputManager.TvInputCallback; import android.os.RemoteException; import android.os.SystemProperties; import android.provider.Settings.Global; @@ -49,6 +54,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; +import com.android.server.SystemService; import java.io.UnsupportedEncodingException; import java.util.ArrayList; @@ -123,6 +129,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // other CEC devices since they might not have logical address. private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>(); + // Message buffer used to buffer selected messages to process later. <Active Source> + // from a source device, for instance, needs to be buffered if the device is not + // discovered yet. The buffered commands are taken out and when they are ready to + // handle. + private final DelayedMessageBuffer mDelayedMessageBuffer = new DelayedMessageBuffer(this); + + // Defines the callback invoked when TV input framework is updated with input status. + // We are interested in the notification for HDMI input addition event, in order to + // process any CEC commands that arrived before the input is added. + private final TvInputCallback mTvInputCallback = new TvInputCallback() { + @Override + public void onInputAdded(String inputId) { + TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId); + HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo(); + if (info != null && info.isCecDevice()) { + mDelayedMessageBuffer.processActiveSource(info.getLogicalAddress()); + } + } + }; + HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_TV); mPrevPortId = Constants.INVALID_PORT_ID; @@ -136,6 +162,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); + mService.registerTvInputCallback(mTvInputCallback); mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, mService.getPhysicalAddress(), mDeviceType)); mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( @@ -407,7 +434,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); if (info == null) { - handleNewDeviceAtTheTailOfActivePath(physicalAddress); + if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) { + mDelayedMessageBuffer.add(message); + } } else { ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); @@ -555,7 +584,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { activeSource.physicalAddress, deviceType)); } - private void handleNewDeviceAtTheTailOfActivePath(int path) { + private boolean handleNewDeviceAtTheTailOfActivePath(int path) { // Seq #22 if (isTailOfActivePath(path, getActivePath())) { removeAction(RoutingControlAction.class); @@ -564,7 +593,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange( mAddress, getActivePath(), newPath)); addAndStartAction(new RoutingControlAction(this, newPath, false, null)); + return true; } + return false; } /** @@ -706,8 +737,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void onNewAvrAdded(HdmiDeviceInfo avr) { assertRunOnServiceThread(); - addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); - if (isArcFeatureEnabled()) { + if (getSystemAudioModeSetting()) { + addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); + } + if (isArcFeatureEnabled() && !hasAction(SetArcTransmissionStateAction.class)) { startArcAction(true); } } @@ -765,7 +798,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } boolean isSystemAudioActivated() { - if (getAvrDeviceInfo() == null) { + if (!hasSystemAudioDevice()) { return false; } synchronized (mLock) { @@ -941,6 +974,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { assertRunOnServiceThread(); if (!canStartArcUpdateAction(message.getSource(), true)) { + if (getAvrDeviceInfo() == null) { + // AVR may not have been discovered yet. Delay the message processing. + mDelayedMessageBuffer.add(message); + return true; + } mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); if (!isConnectedToArcPort(message.getSource())) { displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); @@ -1436,6 +1474,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { super.disableDevice(initiatedByCec, callback); assertRunOnServiceThread(); + mService.unregisterTvInputCallback(mTvInputCallback); // Remove any repeated working actions. // HotplugDetectionAction will be reinstated during the wake up process. // HdmiControlService.onWakeUp() -> initializeLocalDevices() -> @@ -1714,6 +1753,18 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); } + @ServiceThreadOnly + void processAllDelayedMessages() { + assertRunOnServiceThread(); + mDelayedMessageBuffer.processAllMessages(); + } + + @ServiceThreadOnly + void processDelayedMessages(int address) { + assertRunOnServiceThread(); + mDelayedMessageBuffer.processMessagesForDevice(address); + } + @Override protected void dump(final IndentingPrintWriter pw) { super.dump(pw); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 907a49a..8a25f62 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -48,6 +48,8 @@ import android.hardware.hdmi.IHdmiRecordListener; import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import android.hardware.hdmi.IHdmiVendorCommandListener; import android.media.AudioManager; +import android.media.tv.TvInputManager; +import android.media.tv.TvInputManager.TvInputCallback; import android.net.Uri; import android.os.Build; import android.os.Handler; @@ -272,6 +274,9 @@ public final class HdmiControlService extends SystemService { @Nullable private HdmiMhlControllerStub mMhlController; + @Nullable + private TvInputManager mTvInputManager; + // Last input port before switching to the MHL port. Should switch back to this port // when the mobile device sends the request one touch play with off. // Gets invalidated if we go to other port/input. @@ -343,6 +348,28 @@ public final class HdmiControlService extends SystemService { } } + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + mTvInputManager = (TvInputManager) getContext().getSystemService( + Context.TV_INPUT_SERVICE); + } + } + + TvInputManager getTvInputManager() { + return mTvInputManager; + } + + void registerTvInputCallback(TvInputCallback callback) { + if (mTvInputManager == null) return; + mTvInputManager.registerCallback(callback, mHandler); + } + + void unregisterTvInputCallback(TvInputCallback callback) { + if (mTvInputManager == null) return; + mTvInputManager.unregisterCallback(callback); + } + /** * Called when the initialization of local devices is complete. */ diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index 998889b..64f0703 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -161,6 +161,9 @@ final class NewDeviceAction extends HdmiCecFeatureAction { mDeviceType, mVendorId, mDisplayName); tv().addCecDevice(deviceInfo); + // Consume CEC messages we already got for this newly found device. + tv().processDelayedMessages(mDeviceLogicalAddress); + if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { tv().onNewAvrAdded(deviceInfo); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 921b68b..9440697 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -90,6 +90,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final SessionStub mSession; private final SessionCb mSessionCb; private final MediaSessionService mService; + private final boolean mUseMasterVolume; private final Object mLock = new Object(); private final ArrayList<ISessionControllerCallback> mControllerCallbacks = @@ -139,6 +140,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE); mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(); + mUseMasterVolume = service.getContext().getResources().getBoolean( + com.android.internal.R.bool.config_useMasterVolume); } /** @@ -248,6 +251,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { direction = -1; } if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { + if (mUseMasterVolume) { + // If this device only uses master volume and playback is local + // just adjust the master volume and return. + mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName, uid); + return; + } int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); if (useSuggested) { if (AudioSystem.isStreamActive(stream, 0)) { diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 02c9fcb5..98a3970 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -86,6 +86,7 @@ public class MediaSessionService extends SystemService implements Monitor { private final Object mLock = new Object(); private final MessageHandler mHandler = new MessageHandler(); private final PowerManager.WakeLock mMediaEventWakeLock; + private final boolean mUseMasterVolume; private KeyguardManager mKeyguardManager; private IAudioService mAudioService; @@ -104,6 +105,8 @@ public class MediaSessionService extends SystemService implements Monitor { mPriorityStack = new MediaSessionStack(); PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); + mUseMasterVolume = context.getResources().getBoolean( + com.android.internal.R.bool.config_useMasterVolume); } @Override @@ -805,7 +808,12 @@ public class MediaSessionService extends SystemService implements Monitor { + flags + ", suggestedStream=" + suggestedStream); } - if (session == null) { + boolean preferSuggestedStream = false; + if (isValidLocalStreamType(suggestedStream) + && AudioSystem.isStreamActive(suggestedStream, 0)) { + preferSuggestedStream = true; + } + if (session == null || preferSuggestedStream) { if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0 && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { if (DEBUG) { @@ -814,8 +822,13 @@ public class MediaSessionService extends SystemService implements Monitor { return; } try { - mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags, - getContext().getOpPackageName()); + if (mUseMasterVolume) { + mAudioService.adjustMasterVolume(direction, flags, + getContext().getOpPackageName()); + } else { + mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags, + getContext().getOpPackageName()); + } } catch (RemoteException e) { Log.e(TAG, "Error adjusting default volume.", e); } @@ -959,6 +972,12 @@ public class MediaSessionService extends SystemService implements Monitor { return keyCode == KeyEvent.KEYCODE_HEADSETHOOK; } + // we only handle public stream types, which are 0-5 + private boolean isValidLocalStreamType(int streamType) { + return streamType >= AudioManager.STREAM_VOICE_CALL + && streamType <= AudioManager.STREAM_NOTIFICATION; + } + private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler); class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable, diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java index 857b9e9..b5a450d 100644 --- a/services/core/java/com/android/server/net/IpConfigStore.java +++ b/services/core/java/com/android/server/net/IpConfigStore.java @@ -122,8 +122,10 @@ public class IpConfigStore { out.writeUTF(proxyProperties.getHost()); out.writeUTF(PROXY_PORT_KEY); out.writeInt(proxyProperties.getPort()); - out.writeUTF(EXCLUSION_LIST_KEY); - out.writeUTF(exclusionList); + if (exclusionList != null) { + out.writeUTF(EXCLUSION_LIST_KEY); + out.writeUTF(exclusionList); + } written = true; break; case PAC: diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index a1085d3..5de1a64 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -260,9 +260,11 @@ public class ConditionProviders extends ManagedServices { for (int i = 0; i < N; i++) { final Condition c = conditions[i]; final ConditionRecord r = getRecordLocked(c.id, info.component); + final Condition oldCondition = r.condition; + final boolean conditionUpdate = oldCondition != null && !oldCondition.equals(c); r.info = info; r.condition = c; - // if manual, exit zen if false (or failed) + // if manual, exit zen if false (or failed), update if true (and changed) if (r.isManual) { if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) { final boolean failed = c.state == Condition.STATE_ERROR; @@ -275,6 +277,10 @@ public class ConditionProviders extends ManagedServices { "manualConditionExit"); unsubscribeLocked(r); r.isManual = false; + } else if (c.state == Condition.STATE_TRUE && conditionUpdate) { + if (DEBUG) Slog.d(TAG, "Current condition updated, still true. old=" + + oldCondition + " new=" + c); + setZenModeCondition(c, "conditionUpdate"); } } // if automatic, exit zen if false (or failed), enter zen if true @@ -559,7 +565,7 @@ public class ConditionProviders extends ManagedServices { // enter downtime, or update mode if reconfigured during an active downtime if (inDowntime && (mode == Global.ZEN_MODE_OFF || downtimeCurrent) && config != null) { final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(), - Condition.STATE_TRUE); + config.sleepNone, Condition.STATE_TRUE); mZenModeHelper.setZenMode(downtimeMode, "downtimeEnter"); setZenModeCondition(condition, "downtime"); } diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java index 0fb5732..097589a 100644 --- a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java +++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java @@ -122,7 +122,8 @@ public class DowntimeConditionProvider extends ConditionProviderService { if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance); if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) { if (isInDowntime() && mConfig != null) { - notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE)); + notifyCondition(createCondition(mConfig.toDowntimeInfo(), mConfig.sleepNone, + Condition.STATE_TRUE)); } } } @@ -135,7 +136,7 @@ public class DowntimeConditionProvider extends ConditionProviderService { final int state = mConfig.toDowntimeInfo().equals(downtime) && isInDowntime() ? Condition.STATE_TRUE : Condition.STATE_FALSE; if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state)); - notifyCondition(createCondition(downtime, state)); + notifyCondition(createCondition(downtime, mConfig.sleepNone, state)); } } @@ -157,14 +158,22 @@ public class DowntimeConditionProvider extends ConditionProviderService { return mDowntimeMode != Global.ZEN_MODE_OFF; } - public Condition createCondition(DowntimeInfo downtime, int state) { + public Condition createCondition(DowntimeInfo downtime, boolean orAlarm, int state) { if (downtime == null) return null; final Uri id = ZenModeConfig.toDowntimeConditionId(downtime); final String skeleton = DateFormat.is24HourFormat(mContext) ? "Hm" : "hma"; final Locale locale = Locale.getDefault(); final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton); - final long time = getTime(System.currentTimeMillis(), downtime.endHour, downtime.endMinute); - final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(time)); + final long now = System.currentTimeMillis(); + long endTime = getTime(now, downtime.endHour, downtime.endMinute); + if (orAlarm) { + final AlarmClockInfo nextAlarm = mTracker.getNextAlarm(); + final long nextAlarmTime = nextAlarm != null ? nextAlarm.getTriggerTime() : 0; + if (nextAlarmTime > now && nextAlarmTime < endTime) { + endTime = nextAlarmTime; + } + } + final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(endTime)); final String summary = mContext.getString(R.string.downtime_condition_summary, formatted); final String line1 = mContext.getString(R.string.downtime_condition_line_one); return new Condition(id, summary, line1, formatted, 0, state, Condition.FLAG_RELEVANT_NOW); @@ -302,6 +311,11 @@ public class DowntimeConditionProvider extends ConditionProviderService { private void onEvaluateNextAlarm(AlarmClockInfo nextAlarm, long wakeupTime, boolean booted) { if (!booted) return; // we don't know yet + // update condition description if we're in downtime (mode = none) + if (isInDowntime() && mConfig != null && mConfig.sleepNone) { + notifyCondition(createCondition(mConfig.toDowntimeInfo(), true /*orAlarm*/, + Condition.STATE_TRUE)); + } if (nextAlarm == null) return; // not fireable if (DEBUG) Slog.d(TAG, "onEvaluateNextAlarm " + mTracker.formatAlarmDebug(nextAlarm)); if (System.currentTimeMillis() > wakeupTime) { @@ -336,6 +350,10 @@ public class DowntimeConditionProvider extends ConditionProviderService { } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { if (DEBUG) Slog.d(TAG, "timezone changed to " + TimeZone.getDefault()); mCalendar.setTimeZone(TimeZone.getDefault()); + mFiredAlarms.clear(); + } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { + if (DEBUG) Slog.d(TAG, "time changed to " + now); + mFiredAlarms.clear(); } else { if (DEBUG) Slog.d(TAG, action + " fired at " + now); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 42a8551..6958ff8 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1931,12 +1931,7 @@ public class NotificationManagerService extends SystemService { if (hasValidSound) { boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; - AudioAttributes audioAttributes; - if (notification.audioAttributes != null) { - audioAttributes = notification.audioAttributes; - } else { - audioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; - } + AudioAttributes audioAttributes = audioAttributesForNotification(notification); mSoundNotification = record; // do not play notifications if stream volume is 0 (typically because // ringer mode is silent) or if there is a user of exclusive audio focus @@ -2030,7 +2025,9 @@ public class NotificationManagerService extends SystemService { } private static AudioAttributes audioAttributesForNotification(Notification n) { - if (n.audioAttributes != null) { + if (n.audioAttributes != null + && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) { + // the audio attributes are set and different from the default, use them return n.audioAttributes; } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) { // the stream type is valid, use it diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9a2451e..195a886 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4278,7 +4278,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkg.mVersionCode < ps.versionCode) { // The system package has been updated and the code path does not match // Ignore entry. Skip it. - logCriticalInfo(Log.INFO, "Package " + ps.name + " at " + scanFile + Slog.i(TAG, "Package " + ps.name + " at " + scanFile + " ignored: updated version " + ps.versionCode + " better than this " + pkg.mVersionCode); if (!updatedPkg.codePath.equals(scanFile)) { @@ -6437,7 +6437,7 @@ public class PackageManagerService extends IPackageManager.Stub { mResolveActivity.applicationInfo = pkg.applicationInfo; mResolveActivity.name = mCustomResolverComponentName.getClassName(); mResolveActivity.packageName = pkg.applicationInfo.packageName; - mResolveActivity.processName = null; + mResolveActivity.processName = pkg.applicationInfo.packageName; mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS; @@ -8982,6 +8982,7 @@ public class PackageManagerService extends IPackageManager.Stub { && isVerificationEnabled(userIdentifier, installFlags)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); + verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 0cf2249..db0f53b 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1298,7 +1298,12 @@ public class UserManagerService extends IUserManager.Stub { if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) { return false; } + + // We remember deleted user IDs to prevent them from being + // reused during the current boot; they can still be reused + // after a reboot. mRemovingUserIds.put(userHandle, true); + try { mAppOpsService.removeUser(userHandle); } catch (RemoteException e) { @@ -1387,18 +1392,6 @@ public class UserManagerService extends IUserManager.Stub { // Remove this user from the list mUsers.remove(userHandle); - // Have user ID linger for several seconds to let external storage VFS - // cache entries expire. This must be greater than the 'entry_valid' - // timeout used by the FUSE daemon. - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - synchronized (mPackagesLock) { - mRemovingUserIds.delete(userHandle); - } - } - }, MINUTE_IN_MILLIS); - mRestrictionsPinStates.remove(userHandle); // Remove user file AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX)); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index c054e6c..4d8b98f 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1234,6 +1234,7 @@ public final class PowerManagerService extends SystemService // Phase 0: Basic state updates. updateIsPoweredLocked(mDirty); updateStayOnLocked(mDirty); + updateScreenBrightnessBoostLocked(mDirty); // Phase 1: Update wakefulness. // Loop because the wake lock and user activity computations are influenced @@ -1641,7 +1642,8 @@ public final class PowerManagerService extends SystemService || mProximityPositive || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0 || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT - | USER_ACTIVITY_SCREEN_DIM)) != 0; + | USER_ACTIVITY_SCREEN_DIM)) != 0 + || mScreenBrightnessBoostInProgress; } /** @@ -1828,9 +1830,6 @@ public final class PowerManagerService extends SystemService | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) { mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(); - // Handle screen brightness boost timeout. - updateScreenBrightnessBoostLocked(); - // Determine appropriate screen brightness and auto-brightness adjustments. int screenBrightness = mScreenBrightnessSettingDefault; float screenAutoBrightnessAdjustment = 0.0f; @@ -1879,7 +1878,7 @@ public final class PowerManagerService extends SystemService } mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest, - mRequestWaitForNegativeProximity) && !mScreenBrightnessBoostInProgress; + mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; if (DEBUG_SPEW) { @@ -1896,20 +1895,25 @@ public final class PowerManagerService extends SystemService return mDisplayReady && !oldDisplayReady; } - private void updateScreenBrightnessBoostLocked() { - if (mScreenBrightnessBoostInProgress) { - mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT); - if (mLastScreenBrightnessBoostTime > mLastSleepTime) { - final long boostTimeout = mLastScreenBrightnessBoostTime + - SCREEN_BRIGHTNESS_BOOST_TIMEOUT; - if (boostTimeout > SystemClock.uptimeMillis()) { - Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT); - msg.setAsynchronous(true); - mHandler.sendMessageAtTime(msg, boostTimeout); - return; + private void updateScreenBrightnessBoostLocked(int dirty) { + if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) { + if (mScreenBrightnessBoostInProgress) { + final long now = SystemClock.uptimeMillis(); + mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT); + if (mLastScreenBrightnessBoostTime > mLastSleepTime) { + final long boostTimeout = mLastScreenBrightnessBoostTime + + SCREEN_BRIGHTNESS_BOOST_TIMEOUT; + if (boostTimeout > now) { + Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, boostTimeout); + return; + } } + mScreenBrightnessBoostInProgress = false; + userActivityNoUpdateLocked(now, + PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); } - mScreenBrightnessBoostInProgress = false; } } @@ -1940,7 +1944,8 @@ public final class PowerManagerService extends SystemService if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0 - || !mBootCompleted) { + || !mBootCompleted + || mScreenBrightnessBoostInProgress) { return DisplayPowerRequest.POLICY_BRIGHT; } @@ -2037,15 +2042,13 @@ public final class PowerManagerService extends SystemService final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); final boolean autoSuspend = !needDisplaySuspendBlocker; + final boolean interactive = mDisplayPowerRequest.isBrightOrDim(); // Disable auto-suspend if needed. - if (!autoSuspend) { - if (mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(false); - } - if (mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(true); - } + // FIXME We should consider just leaving auto-suspend enabled forever since + // we already hold the necessary wakelocks. + if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(false); } // First acquire suspend blockers if needed. @@ -2058,6 +2061,22 @@ public final class PowerManagerService extends SystemService mHoldingDisplaySuspendBlocker = true; } + // Inform the power HAL about interactive mode. + // Although we could set interactive strictly based on the wakefulness + // as reported by isInteractive(), it is actually more desirable to track + // the display policy state instead so that the interactive state observed + // by the HAL more accurately tracks transitions between AWAKE and DOZING. + // Refer to getDesiredScreenPolicyLocked() for details. + if (mDecoupleHalInteractiveModeFromDisplayConfig) { + // When becoming non-interactive, we want to defer sending this signal + // until the display is actually ready so that all transitions have + // completed. This is probably a good sign that things have gotten + // too tangled over here... + if (interactive || mDisplayReady) { + setHalInteractiveModeLocked(interactive); + } + } + // Then release suspend blockers if needed. if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.release(); @@ -2069,13 +2088,8 @@ public final class PowerManagerService extends SystemService } // Enable auto-suspend if needed. - if (autoSuspend) { - if (mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(false); - } - if (mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(true); - } + if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(true); } } @@ -2097,6 +2111,9 @@ public final class PowerManagerService extends SystemService return true; } } + if (mScreenBrightnessBoostInProgress) { + return true; + } // Let the system suspend if the screen is off or dozing. return false; } diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index 4906bd1..57b204d 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -232,6 +232,12 @@ public class TrustAgentWrapper { mTrustManagerService.mArchive.logAgentConnected(mUserId, name); setCallback(mCallback); updateDevicePolicyFeatures(); + + if (mTrustManagerService.isDeviceLockedInner(mUserId)) { + onDeviceLocked(); + } else { + onDeviceUnlocked(); + } } @Override @@ -287,6 +293,7 @@ public class TrustAgentWrapper { onError(e); } } + /** * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean) */ @@ -298,6 +305,28 @@ public class TrustAgentWrapper { } } + /** + * @see android.service.trust.TrustAgentService#onDeviceLocked() + */ + public void onDeviceLocked() { + try { + if (mTrustAgentService != null) mTrustAgentService.onDeviceLocked(); + } catch (RemoteException e) { + onError(e); + } + } + + /** + * @see android.service.trust.TrustAgentService#onDeviceUnlocked() + */ + public void onDeviceUnlocked() { + try { + if (mTrustAgentService != null) mTrustAgentService.onDeviceUnlocked(); + } catch (RemoteException e) { + onError(e); + } + } + private void setCallback(ITrustAgentServiceCallback callback) { try { if (mTrustAgentService != null) { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 2388c85..a2ea1c8 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -26,7 +26,6 @@ import org.xmlpull.v1.XmlPullParserException; import android.Manifest; import android.app.ActivityManager; -import android.app.ActivityManagerNative; import android.app.admin.DevicePolicyManager; import android.app.trust.ITrustListener; import android.app.trust.ITrustManager; @@ -61,6 +60,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.Xml; +import android.view.IWindowManager; +import android.view.WindowManagerGlobal; import java.io.FileDescriptor; import java.io.IOException; @@ -96,6 +97,10 @@ public class TrustManagerService extends SystemService { private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3; private static final int MSG_ENABLED_AGENTS_CHANGED = 4; private static final int MSG_REQUIRE_CREDENTIAL_ENTRY = 5; + private static final int MSG_KEYGUARD_SHOWING_CHANGED = 6; + private static final int MSG_START_USER = 7; + private static final int MSG_CLEANUP_USER = 8; + private static final int MSG_SWITCH_USER = 9; private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>(); private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>(); @@ -110,7 +115,11 @@ public class TrustManagerService extends SystemService { @GuardedBy("mUserIsTrusted") private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray(); + @GuardedBy("mDeviceLockedForUser") + private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray(); + private boolean mTrustAgentsCanRun = false; + private int mCurrentUser = UserHandle.USER_OWNER; public TrustManagerService(Context context) { super(context); @@ -177,10 +186,15 @@ public class TrustManagerService extends SystemService { public void updateTrust(int userId, boolean initiatedByUser) { dispatchOnTrustManagedChanged(aggregateIsTrustManaged(userId), userId); boolean trusted = aggregateIsTrusted(userId); + boolean changed; synchronized (mUserIsTrusted) { + changed = mUserIsTrusted.get(userId) != trusted; mUserIsTrusted.put(userId, trusted); } dispatchOnTrustChanged(trusted, userId, initiatedByUser); + if (changed) { + refreshDeviceLockedForUser(userId); + } } void refreshAgentList(int userId) { @@ -212,8 +226,7 @@ public class TrustManagerService extends SystemService { || userInfo.guestToRemove) continue; if (!userInfo.supportsSwitchTo()) continue; if (!mActivityManager.isUserRunning(userInfo.id)) continue; - if (lockPatternUtils.getKeyguardStoredPasswordQuality(userInfo.id) - == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) continue; + if (!lockPatternUtils.isSecure(userInfo.id)) continue; if (!mUserHasAuthenticatedSinceBoot.get(userInfo.id)) continue; DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager(); int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id); @@ -273,6 +286,73 @@ public class TrustManagerService extends SystemService { } } + boolean isDeviceLockedInner(int userId) { + synchronized (mDeviceLockedForUser) { + return mDeviceLockedForUser.get(userId, true); + } + } + + private void refreshDeviceLockedForUser(int userId) { + if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_OWNER) { + Log.e(TAG, "refreshAgentList(userId=" + userId + "): Invalid user handle," + + " must be USER_ALL or a specific user.", new Throwable("here")); + userId = UserHandle.USER_ALL; + } + + List<UserInfo> userInfos; + if (userId == UserHandle.USER_ALL) { + userInfos = mUserManager.getUsers(true /* excludeDying */); + } else { + userInfos = new ArrayList<>(); + userInfos.add(mUserManager.getUserInfo(userId)); + } + + IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + + for (int i = 0; i < userInfos.size(); i++) { + UserInfo info = userInfos.get(i); + + if (info == null || info.partial || !info.isEnabled() || info.guestToRemove + || !info.supportsSwitchTo()) { + continue; + } + + int id = info.id; + boolean secure = mLockPatternUtils.isSecure(id); + boolean trusted = aggregateIsTrusted(id); + boolean showingKeyguard = true; + if (mCurrentUser == id) { + try { + showingKeyguard = wm.isKeyguardLocked(); + } catch (RemoteException e) { + } + } + boolean deviceLocked = secure && showingKeyguard && !trusted; + + boolean changed; + synchronized (mDeviceLockedForUser) { + changed = isDeviceLockedInner(id) != deviceLocked; + mDeviceLockedForUser.put(id, deviceLocked); + } + if (changed) { + dispatchDeviceLocked(id, deviceLocked); + } + } + } + + private void dispatchDeviceLocked(int userId, boolean isLocked) { + for (int i = 0; i < mActiveAgents.size(); i++) { + AgentInfo agent = mActiveAgents.valueAt(i); + if (agent.userId == userId) { + if (isLocked) { + agent.agent.onDeviceLocked(); + } else{ + agent.agent.onDeviceUnlocked(); + } + } + } + } + void updateDevicePolicyFeatures() { for (int i = 0; i < mActiveAgents.size(); i++) { AgentInfo info = mActiveAgents.valueAt(i); @@ -540,12 +620,17 @@ public class TrustManagerService extends SystemService { @Override public void onStartUser(int userId) { - refreshAgentList(userId); + mHandler.obtainMessage(MSG_START_USER, userId, 0, null).sendToTarget(); } @Override public void onCleanupUser(int userId) { - refreshAgentList(userId); + mHandler.obtainMessage(MSG_CLEANUP_USER, userId, 0, null).sendToTarget(); + } + + @Override + public void onSwitchUser(int userId) { + mHandler.obtainMessage(MSG_SWITCH_USER, userId, 0, null).sendToTarget(); } // Plumbing @@ -578,6 +663,14 @@ public class TrustManagerService extends SystemService { } @Override + public void reportKeyguardShowingChanged() throws RemoteException { + enforceReportPermission(); + // coalesce refresh messages. + mHandler.removeMessages(MSG_KEYGUARD_SHOWING_CHANGED); + mHandler.sendEmptyMessage(MSG_KEYGUARD_SHOWING_CHANGED); + } + + @Override public void registerTrustListener(ITrustListener trustListener) throws RemoteException { enforceListenerPermission(); mHandler.obtainMessage(MSG_REGISTER_LISTENER, trustListener).sendToTarget(); @@ -590,13 +683,12 @@ public class TrustManagerService extends SystemService { } @Override - public boolean isTrusted(int userId) throws RemoteException { + public boolean isDeviceLocked(int userId) throws RemoteException { userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, - false /* allowAll */, true /* requireFull */, "isTrusted", null); + false /* allowAll */, true /* requireFull */, "isDeviceLocked", null); userId = resolveProfileParent(userId); - synchronized (mUserIsTrusted) { - return mUserIsTrusted.get(userId); - } + + return isDeviceLockedInner(userId); } private void enforceReportPermission() { @@ -621,19 +713,13 @@ public class TrustManagerService extends SystemService { fout.println("disabled because the third-party apps can't run yet."); return; } - final UserInfo currentUser; final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */); - try { - currentUser = ActivityManagerNative.getDefault().getCurrentUser(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } mHandler.runWithScissors(new Runnable() { @Override public void run() { fout.println("Trust manager state:"); for (UserInfo user : userInfos) { - dumpUser(fout, user, user.id == currentUser.id); + dumpUser(fout, user, user.id == mCurrentUser); } } }, 1500); @@ -642,11 +728,17 @@ public class TrustManagerService extends SystemService { private void dumpUser(PrintWriter fout, UserInfo user, boolean isCurrent) { fout.printf(" User \"%s\" (id=%d, flags=%#x)", user.name, user.id, user.flags); + if (!user.supportsSwitchTo()) { + fout.println("(managed profile)"); + fout.println(" disabled because switching to this user is not possible."); + return; + } if (isCurrent) { fout.print(" (current)"); } fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id))); fout.print(", trustManaged=" + dumpBool(aggregateIsTrustManaged(user.id))); + fout.print(", deviceLocked=" + dumpBool(isDeviceLockedInner(user.id))); fout.println(); fout.println(" Enabled agents:"); boolean duplicateSimpleNames = false; @@ -711,10 +803,23 @@ public class TrustManagerService extends SystemService { break; case MSG_ENABLED_AGENTS_CHANGED: refreshAgentList(UserHandle.USER_ALL); + // This is also called when the security mode of a user changes. + refreshDeviceLockedForUser(UserHandle.USER_ALL); break; case MSG_REQUIRE_CREDENTIAL_ENTRY: requireCredentialEntry(msg.arg1); break; + case MSG_KEYGUARD_SHOWING_CHANGED: + refreshDeviceLockedForUser(UserHandle.USER_CURRENT); + break; + case MSG_START_USER: + case MSG_CLEANUP_USER: + refreshAgentList(msg.arg1); + break; + case MSG_SWITCH_USER: + mCurrentUser = msg.arg1; + refreshDeviceLockedForUser(UserHandle.USER_ALL); + break; } } }; @@ -756,8 +861,14 @@ public class TrustManagerService extends SystemService { int userId = getUserId(intent); if (userId > 0) { mUserHasAuthenticatedSinceBoot.delete(userId); - mUserIsTrusted.delete(userId); + synchronized (mUserIsTrusted) { + mUserIsTrusted.delete(userId); + } + synchronized (mDeviceLockedForUser) { + mDeviceLockedForUser.delete(userId); + } refreshAgentList(userId); + refreshDeviceLockedForUser(userId); } } } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 85659cf..716487c 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -299,6 +299,13 @@ class TvInputHardwareManager implements TvInputHal.Callback { return -1; } + private static boolean intArrayContains(int[] array, int value) { + for (int element : array) { + if (element == value) return true; + } + return false; + } + public void addHdmiTvInput(int id, TvInputInfo info) { if (info.getType() != TvInputInfo.TYPE_HDMI) { throw new IllegalArgumentException("info (" + info + ") has non-HDMI type."); @@ -673,28 +680,35 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (mReleased) { throw new IllegalStateException("Device already released."); } - if (surface != null && config == null) { - return false; - } - if (surface == null && mActiveConfig == null) { - return false; - } - int result = TvInputHal.ERROR_UNKNOWN; + int result = TvInputHal.SUCCESS; if (surface == null) { - result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig); - mActiveConfig = null; + // The value of config is ignored when surface == null. + if (mActiveConfig != null) { + result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig); + mActiveConfig = null; + } else { + // We already have no active stream. + return true; + } } else { - if (!config.equals(mActiveConfig)) { + // It's impossible to set a non-null surface with a null config. + if (config == null) { + return false; + } + // Remove stream only if we have an existing active configuration. + if (mActiveConfig != null && !config.equals(mActiveConfig)) { result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig); if (result != TvInputHal.SUCCESS) { mActiveConfig = null; - return false; } } - result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config); + // Proceed only if all previous operations succeeded. if (result == TvInputHal.SUCCESS) { - mActiveConfig = config; + result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config); + if (result == TvInputHal.SUCCESS) { + mActiveConfig = config; + } } } updateAudioConfigLocked(); @@ -755,20 +769,64 @@ class TvInputHardwareManager implements TvInputHal.Callback { AudioPortConfig sinkConfig = mAudioSink.activeConfig(); AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch }; boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated; + + int sinkSamplingRate = mDesiredSamplingRate; + int sinkChannelMask = mDesiredChannelMask; + int sinkFormat = mDesiredFormat; + // If sinkConfig != null and values are set to default, fill in the sinkConfig values. + if (sinkConfig != null) { + if (sinkSamplingRate == 0) { + sinkSamplingRate = sinkConfig.samplingRate(); + } + if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) { + sinkChannelMask = sinkConfig.channelMask(); + } + if (sinkFormat == AudioFormat.ENCODING_DEFAULT) { + sinkChannelMask = sinkConfig.format(); + } + } + if (sinkConfig == null - || (mDesiredSamplingRate != 0 - && sinkConfig.samplingRate() != mDesiredSamplingRate) - || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT - && sinkConfig.channelMask() != mDesiredChannelMask) - || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT - && sinkConfig.format() != mDesiredFormat)) { - sinkConfig = mAudioSink.buildConfig(mDesiredSamplingRate, mDesiredChannelMask, - mDesiredFormat, null); + || sinkConfig.samplingRate() != sinkSamplingRate + || sinkConfig.channelMask() != sinkChannelMask + || sinkConfig.format() != sinkFormat) { + // Check for compatibility and reset to default if necessary. + if (!intArrayContains(mAudioSink.samplingRates(), sinkSamplingRate) + && mAudioSink.samplingRates().length > 0) { + sinkSamplingRate = mAudioSink.samplingRates()[0]; + } + if (!intArrayContains(mAudioSink.channelMasks(), sinkChannelMask)) { + sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT; + } + if (!intArrayContains(mAudioSink.formats(), sinkFormat)) { + sinkFormat = AudioFormat.ENCODING_DEFAULT; + } + sinkConfig = mAudioSink.buildConfig(sinkSamplingRate, sinkChannelMask, + sinkFormat, null); shouldRecreateAudioPatch = true; } if (sourceConfig == null || sourceGainConfig != null) { - sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(), - sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig); + int sourceSamplingRate = 0; + if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) { + sourceSamplingRate = sinkConfig.samplingRate(); + } else if (mAudioSource.samplingRates().length > 0) { + // Use any sampling rate and hope audio patch can handle resampling... + sourceSamplingRate = mAudioSource.samplingRates()[0]; + } + int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT; + for (int inChannelMask : mAudioSource.channelMasks()) { + if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask()) + == AudioFormat.channelCountFromInChannelMask(inChannelMask)) { + sourceChannelMask = inChannelMask; + break; + } + } + int sourceFormat = AudioFormat.ENCODING_DEFAULT; + if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) { + sourceFormat = sinkConfig.format(); + } + sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask, + sourceFormat, sourceGainConfig); shouldRecreateAudioPatch = true; } if (shouldRecreateAudioPatch) { @@ -778,6 +836,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { new AudioPortConfig[] { sourceConfig }, new AudioPortConfig[] { sinkConfig }); mAudioPatch = audioPatchArray[0]; + if (sourceGainConfig != null) { + mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig); + } } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index f947b6a..0cbf03a 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -984,12 +984,7 @@ final class AccessibilityController { final int visibleWindowCount = visibleWindows.size(); for (int i = visibleWindowCount - 1; i >= 0; i--) { - WindowState windowState = visibleWindows.valueAt(i); - - // Compute the bounds in the screen. - Rect boundsInScreen = mTempRect; - computeWindowBoundsInScreen(windowState, boundsInScreen); - + final WindowState windowState = visibleWindows.valueAt(i); final int flags = windowState.mAttrs.flags; // If the window is not touchable - ignore. @@ -997,6 +992,10 @@ final class AccessibilityController { continue; } + // Compute the bounds in the screen. + final Rect boundsInScreen = mTempRect; + computeWindowBoundsInScreen(windowState, boundsInScreen); + // If the window is completely covered by other windows - ignore. if (unaccountedSpace.quickReject(boundsInScreen)) { continue; @@ -1016,8 +1015,8 @@ final class AccessibilityController { // Account for the space this window takes if the window // is not an accessibility overlay which does not change // the reported windows. - if (windowState.mAttrs.type == WindowManager.LayoutParams - .TYPE_ACCESSIBILITY_OVERLAY) { + if (windowState.mAttrs.type != + WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { unaccountedSpace.op(boundsInScreen, unaccountedSpace, Region.Op.REVERSE_DIFFERENCE); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 1086eb2..f859fd2 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -254,15 +254,18 @@ class AppWindowToken extends WindowToken { @Override void removeAllWindows() { - int winNdx; - while ((winNdx = allAppWindows.size()) > 0) { - WindowState win = allAppWindows.get(winNdx - 1); + for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; + // removeWindowLocked at bottom of loop may remove multiple entries from + // allAppWindows if the window to be removed has child windows. It also may + // not remove any windows from allAppWindows at all if win is exiting and + // currently animating away. This ensures that winNdx is monotonically decreasing + // and never beyond allAppWindows bounds. + winNdx = Math.min(winNdx - 1, allAppWindows.size() - 1)) { + WindowState win = allAppWindows.get(winNdx); if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) { Slog.w(WindowManagerService.TAG, "removeAllWindows: removing win=" + win); } - // {@link WindowManagerService.removeWindowLocked} may remove multiple entries from - // {@link #allAppWindows} if the window to be removed has child windows. win.mService.removeWindowLocked(win.mSession, win); } } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 345e8af..fe2e0a6 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -221,6 +221,24 @@ public class WindowAnimator { } } + private boolean shouldForceHide(WindowState win) { + final WindowState imeTarget = mService.mInputMethodTarget; + final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() && + (imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0; + + final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw(); + final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ? + null : winShowWhenLocked.mAppToken; + final boolean hideWhenLocked = + !(((win.mIsImWindow || imeTarget == win) && showImeOverKeyguard) + || (appShowWhenLocked != null && (appShowWhenLocked == win.mAppToken || + // Show error dialogs over apps that dismiss keyguard. + (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0))); + return ((mForceHiding == KEYGUARD_ANIMATING_IN) + && (!win.mWinAnimator.isAnimating() || hideWhenLocked)) + || ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked); + } + private void updateWindowsLocked(final int displayId) { ++mAnimTransactionSequence; @@ -256,14 +274,6 @@ public class WindowAnimator { mForceHiding = KEYGUARD_NOT_SHOWN; - final WindowState imeTarget = mService.mInputMethodTarget; - final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() && - (imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0; - - final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw(); - final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ? - null : winShowWhenLocked.mAppToken; - boolean wallpaperInUnForceHiding = false; boolean startingInUnForceHiding = false; ArrayList<WindowStateAnimator> unForceHiding = null; @@ -272,7 +282,8 @@ public class WindowAnimator { WindowState win = windows.get(i); WindowStateAnimator winAnimator = win.mWinAnimator; final int flags = win.mAttrs.flags; - + boolean canBeForceHidden = mPolicy.canBeForceHidden(win, win.mAttrs); + boolean shouldBeForceHidden = shouldForceHide(win); if (winAnimator.mSurfaceControl != null) { final boolean wasAnimating = winAnimator.mWasAnimating; final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime); @@ -332,15 +343,8 @@ public class WindowAnimator { + " vis=" + win.mViewVisibility + " hidden=" + win.mRootToken.hidden + " anim=" + win.mWinAnimator.mAnimation); - } else if (mPolicy.canBeForceHidden(win, win.mAttrs)) { - final boolean hideWhenLocked = - !(((win.mIsImWindow || imeTarget == win) && showImeOverKeyguard) - || (appShowWhenLocked != null && (appShowWhenLocked == win.mAppToken || - // Show error dialogs over apps that dismiss keyguard. - (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0))); - if (((mForceHiding == KEYGUARD_ANIMATING_IN) - && (!winAnimator.isAnimating() || hideWhenLocked)) - || ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) { + } else if (canBeForceHidden) { + if (shouldBeForceHidden) { if (!win.hideLw(false, false)) { // Was already hidden continue; @@ -411,6 +415,16 @@ public class WindowAnimator { } } + // If the window doesn't have a surface, the only thing we care about is the correct + // policy visibility. + else if (canBeForceHidden) { + if (shouldBeForceHidden) { + win.hideLw(false, false); + } else { + win.showLw(false, false); + } + } + final AppWindowToken atoken = win.mAppToken; if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) { if (atoken == null || atoken.allDrawn) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index eb418ed..bcfd7f0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2541,7 +2541,7 @@ public class WindowManagerService extends IWindowManager.Stub } mInputMonitor.updateInputWindowsLw(false /*force*/); - if (true || localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client " + if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client " + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5)); if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) { @@ -2706,8 +2706,7 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.removeWindowLw(win); win.removeLocked(); - if (true || DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win + - " Callers=" + Debug.getCallers(5)); + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win); mWindowMap.remove(win.mClient.asBinder()); if (win.mAppOp != AppOpsManager.OP_NONE) { mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage()); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6bf2606..b97f4f9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -901,23 +901,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (DBG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle); DevicePolicyData policy = getUserData(userHandle); IPackageManager pm = AppGlobals.getPackageManager(); - for (int i = policy.mAdminList.size() - 1; i >= 0; i--) { - ActiveAdmin aa = policy.mAdminList.get(i); - try { - if (pm.getPackageInfo(aa.info.getPackageName(), 0, userHandle) == null - || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle) == null) { - removed = true; - policy.mAdminList.remove(i); - policy.mAdminMap.remove(aa.info.getComponent()); + synchronized (this) { + for (int i = policy.mAdminList.size() - 1; i >= 0; i--) { + ActiveAdmin aa = policy.mAdminList.get(i); + try { + if (pm.getPackageInfo(aa.info.getPackageName(), 0, userHandle) == null + || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle) == null) { + removed = true; + policy.mAdminList.remove(i); + policy.mAdminMap.remove(aa.info.getComponent()); + } + } catch (RemoteException re) { + // Shouldn't happen } - } catch (RemoteException re) { - // Shouldn't happen } - } - if (removed) { - validatePasswordOwnerLocked(policy); - syncDeviceCapabilitiesLocked(policy); - saveSettingsLocked(policy.mUserHandle); + if (removed) { + validatePasswordOwnerLocked(policy); + syncDeviceCapabilitiesLocked(policy); + saveSettingsLocked(policy.mUserHandle); + } } } @@ -3934,7 +3936,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int n = policy.mAdminList.size(); for (int i = 0; i < n; i++) { ActiveAdmin admin = policy.mAdminList.get(i); - if (profileOwner.equals(admin.info)) { + if (profileOwner.equals(admin.info.getComponent())) { return admin; } } @@ -5051,13 +5053,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean isUninstallBlocked(ComponentName who, String packageName) { + // This function should return true if and only if the package is blocked by + // setUninstallBlocked(). It should still return false for other cases of blocks, such as + // when the package is a system app, or when it is an active device admin. final int userId = UserHandle.getCallingUserId(); synchronized (this) { - if (who == null) { - throw new NullPointerException("ComponentName is null"); + if (who != null) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = Binder.clearCallingIdentity(); try { diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java index cc5d004..1cf00d2 100644 --- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java +++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java @@ -207,7 +207,12 @@ public class UsbDebuggingManager implements Runnable { case MESSAGE_ADB_CONFIRM: { String key = (String)msg.obj; - mFingerprints = getFingerprints(key); + String fingerprints = getFingerprints(key); + if ("".equals(fingerprints)) { + sendResponse("NO"); + break; + } + mFingerprints = fingerprints; startConfirmation(key, mFingerprints); break; } @@ -224,16 +229,25 @@ public class UsbDebuggingManager implements Runnable { StringBuilder sb = new StringBuilder(); MessageDigest digester; + if (key == null) { + return ""; + } + try { digester = MessageDigest.getInstance("MD5"); } catch (Exception ex) { - Slog.e(TAG, "Error getting digester: " + ex); + Slog.e(TAG, "Error getting digester", ex); return ""; } byte[] base64_data = key.split("\\s+")[0].getBytes(); - byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT)); - + byte[] digest; + try { + digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT)); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "error doing base64 decoding", e); + return ""; + } for (int i = 0; i < digest.length; i++) { sb.append(hex.charAt((digest[i] >> 4) & 0xf)); sb.append(hex.charAt(digest[i] & 0xf)); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index c63eb18..23ba3b6 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -209,8 +209,13 @@ public class UsbDeviceManager { mUseUsbNotification = !massStorageSupported; // make sure the ADB_ENABLED setting value matches the current state - Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0); - + try { + Settings.Global.putInt(mContentResolver, + Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0); + } catch (SecurityException e) { + // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed. + Slog.d(TAG, "ADB_ENABLED is restricted."); + } mHandler.sendEmptyMessage(MSG_SYSTEM_READY); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 82b7f8b..f5d4867 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -136,11 +136,14 @@ public class VoiceInteractionManagerService extends SystemService { Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle); ComponentName curRecognizer = getCurRecognizer(userHandle); VoiceInteractionServiceInfo curInteractorInfo = null; - if (curInteractorStr == null && curRecognizer != null) { + if (curInteractorStr == null && curRecognizer != null + && !ActivityManager.isLowRamDeviceStatic()) { // If there is no interactor setting, that means we are upgrading // from an older platform version. If the current recognizer is not // set or matches the preferred recognizer, then we want to upgrade // the user to have the default voice interaction service enabled. + // Note that we don't do this for low-RAM devices, since we aren't + // supporting voice interaction services there. curInteractorInfo = findAvailInteractor(userHandle, curRecognizer); if (curInteractorInfo != null) { // Looks good! We'll apply this one. To make it happen, we clear the @@ -150,6 +153,15 @@ public class VoiceInteractionManagerService extends SystemService { } } + // If we are on a svelte device, make sure an interactor is not currently + // enabled; if it is, turn it off. + if (ActivityManager.isLowRamDeviceStatic() && curInteractorStr != null) { + if (!TextUtils.isEmpty(curInteractorStr)) { + setCurInteractor(null, userHandle); + curInteractorStr = ""; + } + } + if (curRecognizer != null) { // If we already have at least a recognizer, then we probably want to // leave things as they are... unless something has disappeared. @@ -171,10 +183,11 @@ public class VoiceInteractionManagerService extends SystemService { } } - // Initializing settings, look for an interactor first. - if (curInteractorInfo == null) { + // Initializing settings, look for an interactor first (but only on non-svelte). + if (curInteractorInfo == null && !ActivityManager.isLowRamDeviceStatic()) { curInteractorInfo = findAvailInteractor(userHandle, null); } + if (curInteractorInfo != null) { // Eventually it will be an error to not specify this. setCurInteractor(new ComponentName(curInteractorInfo.getServiceInfo().packageName, |