diff options
Diffstat (limited to 'services')
64 files changed, 2110 insertions, 865 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 5ae26ef..93c65f3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -170,6 +170,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Rect mTempRect = new Rect(); + private final Rect mTempRect1 = new Rect(); + private final Point mTempPoint = new Point(); private final PackageManager mPackageManager; @@ -765,16 +767,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } /** - * Gets the bounds of the active window. + * Gets the bounds of a window. * * @param outBounds The output to which to write the bounds. */ - boolean getActiveWindowBounds(Rect outBounds) { - // TODO: This should be refactored to work with accessibility - // focus in multiple windows. + boolean getWindowBounds(int windowId, Rect outBounds) { IBinder token; synchronized (mLock) { - final int windowId = mSecurityPolicy.mActiveWindowId; token = mGlobalWindowTokens.get(windowId); if (token == null) { token = getCurrentUserStateLocked().mWindowTokens.get(windowId); @@ -2536,57 +2535,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override - public boolean computeClickPointInScreen(int accessibilityWindowId, - long accessibilityNodeId, int interactionId, - IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) - throws RemoteException { - final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; - Region partialInteractiveRegion = mTempRegion; - synchronized (mLock) { - // We treat calls from a profile as if made by its parent as profiles - // share the accessibility state of the parent. The call below - // performs the current profile parent resolution. - final int resolvedUserId = mSecurityPolicy - .resolveCallingUserIdEnforcingPermissionsLocked( - UserHandle.USER_CURRENT); - if (resolvedUserId != mCurrentUserId) { - return false; - } - resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); - final boolean permissionGranted = - mSecurityPolicy.canRetrieveWindowContentLocked(this); - if (!permissionGranted) { - return false; - } else { - connection = getConnectionLocked(resolvedWindowId); - if (connection == null) { - return false; - } - } - if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( - resolvedWindowId, partialInteractiveRegion)) { - partialInteractiveRegion = null; - } - } - final int interrogatingPid = Binder.getCallingPid(); - final long identityToken = Binder.clearCallingIdentity(); - MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId); - try { - connection.computeClickPointInScreen(accessibilityNodeId, partialInteractiveRegion, - interactionId, callback, interrogatingPid, interrogatingTid, spec); - return true; - } catch (RemoteException re) { - if (DEBUG) { - Slog.e(LOG_TAG, "Error computeClickPointInScreen()."); - } - } finally { - Binder.restoreCallingIdentity(identityToken); - } - return false; - } - - @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP); synchronized (mLock) { @@ -3239,38 +3187,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } synchronized (mLock) { - Point point = mClient.computeClickPointInScreen(mConnectionId, - focus.getWindowId(), focus.getSourceNodeId()); - - if (point == null) { + Rect boundsInScreen = mTempRect; + focus.getBoundsInScreen(boundsInScreen); + + // Clip to the window bounds. + Rect windowBounds = mTempRect1; + getWindowBounds(focus.getWindowId(), windowBounds); + boundsInScreen.intersect(windowBounds); + if (boundsInScreen.isEmpty()) { return false; } + // Apply magnification if needed. MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId()); if (spec != null && !spec.isNop()) { - point.offset((int) -spec.offsetX, (int) -spec.offsetY); - point.x = (int) (point.x * (1 / spec.scale)); - point.y = (int) (point.y * (1 / spec.scale)); + boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY); + boundsInScreen.scale(1 / spec.scale); } - // Make sure the point is within the window. - Rect windowBounds = mTempRect; - getActiveWindowBounds(windowBounds); - if (!windowBounds.contains(point.x, point.y)) { - return false; - } - - // Make sure the point is within the screen. + // Clip to the screen bounds. Point screenSize = mTempPoint; mDefaultDisplay.getRealSize(screenSize); - if (point.x < 0 || point.x > screenSize.x - || point.y < 0 || point.y > screenSize.y) { + boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y); + if (boundsInScreen.isEmpty()) { return false; } - outPoint.set(point.x, point.y); - return true; + outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY()); } + + return true; } private AccessibilityNodeInfo getAccessibilityFocusNotLocked() { diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index b9ed89b..f18b5ef 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -77,6 +77,10 @@ class TouchExplorer implements EventStreamTransformation { private static final int STATE_DELEGATING = 0x00000004; private static final int STATE_GESTURE_DETECTING = 0x00000005; + private static final int CLICK_LOCATION_NONE = 0; + private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1; + private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2; + // The maximum of the cosine between the vectors of two moving // pointers so they can be considered moving in the same direction. private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) @@ -942,12 +946,16 @@ class TouchExplorer implements EventStreamTransformation { * * @param prototype The prototype from which to create the injected events. * @param policyFlags The policy flags associated with the event. + * @param targetAccessibilityFocus Whether the event targets the accessibility focus. */ - private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) { + private void sendActionDownAndUp(MotionEvent prototype, int policyFlags, + boolean targetAccessibilityFocus) { // Tap with the pointer that last explored. final int pointerId = prototype.getPointerId(prototype.getActionIndex()); final int pointerIdBits = (1 << pointerId); + prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); + prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } @@ -1155,7 +1163,8 @@ class TouchExplorer implements EventStreamTransformation { final int pointerIndex = secondTapUp.findPointerIndex(pointerId); Point clickLocation = mTempPoint; - if (!computeClickLocation(clickLocation)) { + final int result = computeClickLocation(clickLocation); + if (result == CLICK_LOCATION_NONE) { return; } @@ -1171,7 +1180,8 @@ class TouchExplorer implements EventStreamTransformation { secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties, coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0, secondTapUp.getSource(), secondTapUp.getFlags()); - sendActionDownAndUp(event, policyFlags); + final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS); + sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus); event.recycle(); } @@ -1216,7 +1226,7 @@ class TouchExplorer implements EventStreamTransformation { MAX_DRAGGING_ANGLE_COS); } - private boolean computeClickLocation(Point outLocation) { + private int computeClickLocation(Point outLocation) { MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick(); if (lastExploreEvent != null) { final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); @@ -1224,14 +1234,17 @@ class TouchExplorer implements EventStreamTransformation { outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex); if (!mAms.accessibilityFocusOnlyInActiveWindow() || mLastTouchedWindowId == mAms.getActiveWindowId()) { - mAms.getAccessibilityFocusClickPointInScreen(outLocation); + if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { + return CLICK_LOCATION_ACCESSIBILITY_FOCUS; + } else { + return CLICK_LOCATION_LAST_TOUCH_EXPLORED; + } } - return true; } if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { - return true; + return CLICK_LOCATION_ACCESSIBILITY_FOCUS; } - return false; + return CLICK_LOCATION_NONE; } /** @@ -1310,14 +1323,13 @@ class TouchExplorer implements EventStreamTransformation { return; } - int clickLocationX; - int clickLocationY; - final int pointerId = mEvent.getPointerId(mEvent.getActionIndex()); final int pointerIndex = mEvent.findPointerIndex(pointerId); Point clickLocation = mTempPoint; - if (!computeClickLocation(clickLocation)) { + final int result = computeClickLocation(clickLocation); + + if (result == CLICK_LOCATION_NONE) { return; } diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 6b8b5b1..c1e4994 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -130,6 +130,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.TreeMap; @@ -381,6 +382,7 @@ public class BackupManagerService { // we're now good to go, so start the backup alarms if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups"); startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL); + scheduleNextFullBackupJob(); } } } @@ -2695,6 +2697,84 @@ public class BackupManagerService { } } + // SHA-1 a byte array and return the result in hex + private String SHA1Checksum(byte[] input) { + final byte[] checksum; + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + checksum = md.digest(input); + } catch (NoSuchAlgorithmException e) { + Slog.e(TAG, "Unable to use SHA-1!"); + return "00"; + } + + StringBuffer sb = new StringBuffer(checksum.length * 2); + for (int i = 0; i < checksum.length; i++) { + sb.append(Integer.toHexString(checksum[i])); + } + return sb.toString(); + } + + private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName) + throws IOException { + byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, + UserHandle.USER_OWNER); + // has the widget state changed since last time? + final File widgetFile = new File(mStateDir, pkgName + "_widget"); + final boolean priorStateExists = widgetFile.exists(); + + if (MORE_DEBUG) { + if (priorStateExists || widgetState != null) { + Slog.i(TAG, "Checking widget update: state=" + (widgetState != null) + + " prior=" + priorStateExists); + } + } + + if (!priorStateExists && widgetState == null) { + // no prior state, no new state => nothing to do + return; + } + + // if the new state is not null, we might need to compare checksums to + // determine whether to update the widget blob in the archive. If the + // widget state *is* null, we know a priori at this point that we simply + // need to commit a deletion for it. + String newChecksum = null; + if (widgetState != null) { + newChecksum = SHA1Checksum(widgetState); + if (priorStateExists) { + final String priorChecksum; + try ( + FileInputStream fin = new FileInputStream(widgetFile); + DataInputStream in = new DataInputStream(fin) + ) { + priorChecksum = in.readUTF(); + } + if (Objects.equals(newChecksum, priorChecksum)) { + // Same checksum => no state change => don't rewrite the widget data + return; + } + } + } // else widget state *became* empty, so we need to commit a deletion + + BackupDataOutput out = new BackupDataOutput(fd); + if (widgetState != null) { + try ( + FileOutputStream fout = new FileOutputStream(widgetFile); + DataOutputStream stateOut = new DataOutputStream(fout) + ) { + stateOut.writeUTF(newChecksum); + } + + out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length); + out.writeEntityData(widgetState, widgetState.length); + } else { + // Widget state for this app has been removed; commit a deletion + out.writeEntityHeader(KEY_WIDGET_STATE, -1); + widgetFile.delete(); + } + } + @Override public void operationComplete() { // Okay, the agent successfully reported back to us! @@ -2733,17 +2813,7 @@ public class BackupManagerService { } // Piggyback the widget state payload, if any - BackupDataOutput out = new BackupDataOutput(fd); - byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, - UserHandle.USER_OWNER); - if (widgetState != null) { - out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length); - out.writeEntityData(widgetState, widgetState.length); - } else { - // No widget state for this app, but push a 'delete' operation for it - // in case they're trying to play games with the payload. - out.writeEntityHeader(KEY_WIDGET_STATE, -1); - } + writeWidgetPayloadIfAppropriate(fd, pkgName); } catch (IOException e) { // Hard disk error; recovery/failure policy TBD. For now roll back, // but we may want to consider this a transport-level failure (i.e. @@ -2786,21 +2856,30 @@ public class BackupManagerService { addBackupTrace("finishing op on transport"); mStatus = mTransport.finishBackup(); addBackupTrace("finished: " + mStatus); + } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { + addBackupTrace("transport rejected package"); } } else { if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport"); addBackupTrace("no data to send"); } - // After successful transport, delete the now-stale data - // and juggle the files so that next time we supply the agent - // with the new state file it just created. if (mStatus == BackupTransport.TRANSPORT_OK) { + // After successful transport, delete the now-stale data + // and juggle the files so that next time we supply the agent + // with the new state file it just created. mBackupDataName.delete(); mNewStateName.renameTo(mSavedStateName); EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size); logBackupComplete(pkgName); + } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { + // The transport has rejected backup of this specific package. Roll it + // back but proceed with running the rest of the queue. + mBackupDataName.delete(); + mNewStateName.delete(); + EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected"); } else { + // Actual transport-level failure to communicate the data to the backend EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); } } catch (Exception e) { @@ -2811,15 +2890,17 @@ public class BackupManagerService { try { if (backupData != null) backupData.close(); } catch (IOException e) {} } - // If we encountered an error here it's a transport-level failure. That - // means we need to halt everything and reschedule everything for next time. final BackupState nextState; - if (mStatus != BackupTransport.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK + || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { + // Success or single-package rejection. Proceed with the next app if any, + // otherwise we're done. + nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; + } else { + // Any other error here indicates a transport-level failure. That means + // we need to halt everything and reschedule everything for next time. revertAndEndBackup(); nextState = BackupState.FINAL; - } else { - // Success! Proceed with the next app if any, otherwise we're done. - nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; } executeNextState(nextState); @@ -3773,6 +3854,16 @@ public class BackupManagerService { PackageInfo currentPackage; try { + if (!mEnabled || !mProvisioned) { + // Backups are globally disabled, so don't proceed. + if (DEBUG) { + Slog.i(TAG, "full backup requested but e=" + mEnabled + + " p=" + mProvisioned + "; ignoring"); + } + mUpdateSchedule = false; + return; + } + IBackupTransport transport = getTransport(mCurrentTransport); if (transport == null) { Slog.w(TAG, "Transport not present; full data backup not performed"); @@ -4070,6 +4161,17 @@ public class BackupManagerService { long now = System.currentTimeMillis(); FullBackupEntry entry = null; + if (!mEnabled || !mProvisioned) { + // Backups are globally disabled, so don't proceed. We also don't reschedule + // the job driving automatic backups; that job will be scheduled again when + // the user enables backup. + if (MORE_DEBUG) { + Slog.i(TAG, "beginFullBackup but e=" + mEnabled + + " p=" + mProvisioned + "; ignoring"); + } + return false; + } + if (DEBUG_SCHEDULING) { Slog.i(TAG, "Beginning scheduled full backup operation"); } @@ -7963,8 +8065,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // Record that we need a backup pass for the caller. Since multiple callers // may share a uid, we need to note all candidates within that uid and schedule // a backup pass for each of them. - EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName); - if (targets == null) { Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" + " uid=" + Binder.getCallingUid()); diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index a9a756e..65a5c23 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1230,7 +1230,8 @@ class AlarmManagerService extends SystemService { if (mAlarmBatches.size() > 0) { final Batch firstWakeup = findFirstWakeupBatchLocked(); final Batch firstBatch = mAlarmBatches.get(0); - if (firstWakeup != null && mNextWakeup != firstWakeup.start) { + // always update the kernel alarms, as a backstop against missed wakeups + if (firstWakeup != null) { mNextWakeup = firstWakeup.start; setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); } @@ -1243,7 +1244,8 @@ class AlarmManagerService extends SystemService { nextNonWakeup = mNextNonWakeupDeliveryTime; } } - if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) { + // always update the kernel alarm, as a backstop against missed wakeups + if (nextNonWakeup != 0) { mNextNonWakeup = nextNonWakeup; setLocked(ELAPSED_REALTIME, nextNonWakeup); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 766e4c7..b72b29d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -832,6 +832,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } }; + private Network[] getVpnUnderlyingNetworks(int uid) { + if (!mLockdownEnabled) { + int user = UserHandle.getUserId(uid); + synchronized (mVpns) { + Vpn vpn = mVpns.get(user); + if (vpn != null && vpn.appliesToUid(uid)) { + return vpn.getUnderlyingNetworks(); + } + } + } + return null; + } + private NetworkState getUnfilteredActiveNetworkState(int uid) { NetworkInfo info = null; LinkProperties lp = null; @@ -841,25 +854,17 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId); - if (!mLockdownEnabled) { - int user = UserHandle.getUserId(uid); - synchronized (mVpns) { - Vpn vpn = mVpns.get(user); - if (vpn != null && vpn.appliesToUid(uid)) { - // getUnderlyingNetworks() returns: - // null => the VPN didn't specify anything, so we use the default. - // empty array => the VPN explicitly said "no default network". - // non-empty array => the VPN specified one or more default networks; we use the - // first one. - Network[] networks = vpn.getUnderlyingNetworks(); - if (networks != null) { - if (networks.length > 0) { - nai = getNetworkAgentInfoForNetwork(networks[0]); - } else { - nai = null; - } - } - } + final Network[] networks = getVpnUnderlyingNetworks(uid); + if (networks != null) { + // getUnderlyingNetworks() returns: + // null => there was no VPN, or the VPN didn't specify anything, so we use the default. + // empty array => the VPN explicitly said "no default network". + // non-empty array => the VPN specified one or more default networks; we use the + // first one. + if (networks.length > 0) { + nai = getNetworkAgentInfoForNetwork(networks[0]); + } else { + nai = null; } } @@ -907,7 +912,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // network is blocked; clone and override state info = new NetworkInfo(info); info.setDetailedState(DetailedState.BLOCKED, null, null); - if (DBG) log("returning Blocked NetworkInfo"); + if (DBG) { + log("returning Blocked NetworkInfo for ifname=" + + lp.getInterfaceName() + ", uid=" + uid); + } } if (info != null && mLockdownTracker != null) { info = mLockdownTracker.augmentNetworkInfo(info); @@ -987,6 +995,15 @@ public class ConnectivityService extends IConnectivityManager.Stub public NetworkInfo getNetworkInfo(int networkType) { enforceAccessPermission(); final int uid = Binder.getCallingUid(); + if (getVpnUnderlyingNetworks(uid) != null) { + // A VPN is active, so we may need to return one of its underlying networks. This + // information is not available in LegacyTypeTracker, so we have to get it from + // getUnfilteredActiveNetworkState. + NetworkState state = getUnfilteredActiveNetworkState(uid); + if (state.networkInfo != null && state.networkInfo.getType() == networkType) { + return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid); + } + } NetworkState state = getFilteredNetworkState(networkType, uid); return state.networkInfo; } @@ -2069,6 +2086,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // may trigger a re-evaluation of the network. private void unlinger(NetworkAgentInfo nai) { if (VDBG) log("Canceling linger of " + nai.name()); + // If network has never been validated, it cannot have been lingered, so don't bother + // needlessly triggering a re-evaluation. + if (!nai.everValidated) return; nai.networkLingered.clear(); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); } @@ -2268,6 +2288,46 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // Is nai unneeded by all NetworkRequests (and should be disconnected)? + // For validated Networks this is simply whether it is satsifying any NetworkRequests. + // For unvalidated Networks this is whether it is satsifying any NetworkRequests or + // were it to become validated, would it have a chance of satisfying any NetworkRequests. + private boolean unneeded(NetworkAgentInfo nai) { + if (!nai.created || nai.isVPN()) return false; + boolean unneeded = true; + if (nai.everValidated) { + for (int i = 0; i < nai.networkRequests.size() && unneeded; i++) { + final NetworkRequest nr = nai.networkRequests.valueAt(i); + try { + if (isRequest(nr)) unneeded = false; + } catch (Exception e) { + loge("Request " + nr + " not found in mNetworkRequests."); + loge(" it came from request list of " + nai.name()); + } + } + } else { + for (NetworkRequestInfo nri : mNetworkRequests.values()) { + // If this Network is already the highest scoring Network for a request, or if + // there is hope for it to become one if it validated, then it is needed. + if (nri.isRequest && nai.satisfies(nri.request) && + (nai.networkRequests.get(nri.request.requestId) != null || + // Note that this catches two important cases: + // 1. Unvalidated cellular will not be reaped when unvalidated WiFi + // is currently satisfying the request. This is desirable when + // cellular ends up validating but WiFi does not. + // 2. Unvalidated WiFi will not be reaped when validated cellular + // is currently satsifying the request. This is desirable when + // WiFi ends up validating and out scoring cellular. + mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < + nai.getCurrentScoreAsValidated())) { + unneeded = false; + break; + } + } + } + return unneeded; + } + private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { NetworkRequestInfo nri = mNetworkRequests.get(request); if (nri != null) { @@ -2281,6 +2341,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nri.isRequest) { // Find all networks that are satisfying this request and remove the request // from their request lists. + // TODO - it's my understanding that for a request there is only a single + // network satisfying it, so this loop is wasteful + boolean wasKept = false; for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { if (nai.networkRequests.get(nri.request.requestId) != null) { nai.networkRequests.remove(nri.request.requestId); @@ -2289,29 +2352,42 @@ public class ConnectivityService extends IConnectivityManager.Stub ", leaving " + nai.networkRequests.size() + " requests."); } - // check if has any requests remaining and if not, - // disconnect (unless it's a VPN). - boolean keep = nai.isVPN(); - for (int i = 0; i < nai.networkRequests.size() && !keep; i++) { - NetworkRequest r = nai.networkRequests.valueAt(i); - if (isRequest(r)) keep = true; - } - if (!keep) { + if (unneeded(nai)) { if (DBG) log("no live requests for " + nai.name() + "; disconnecting"); - nai.asyncChannel.disconnect(); + teardownUnneededNetwork(nai); + } else { + // suspect there should only be one pass through here + // but if any were kept do the check below + wasKept |= true; } } } + NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); + if (nai != null) { + mNetworkForRequestId.remove(nri.request.requestId); + } // Maintain the illusion. When this request arrived, we might have pretended // that a network connected to serve it, even though the network was already // connected. Now that this request has gone away, we might have to pretend // that the network disconnected. LegacyTypeTracker will generate that // phantom disconnect for this type. - NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); - if (nai != null) { - mNetworkForRequestId.remove(nri.request.requestId); - if (nri.request.legacyType != TYPE_NONE) { + if (nri.request.legacyType != TYPE_NONE && nai != null) { + boolean doRemove = true; + if (wasKept) { + // check if any of the remaining requests for this network are for the + // same legacy type - if so, don't remove the nai + for (int i = 0; i < nai.networkRequests.size(); i++) { + NetworkRequest otherRequest = nai.networkRequests.valueAt(i); + if (otherRequest.legacyType == nri.request.legacyType && + isRequest(otherRequest)) { + if (DBG) log(" still have other legacy request - leaving"); + doRemove = false; + } + } + } + + if (doRemove) { mLegacyTypeTracker.remove(nri.request.legacyType, nai); } } @@ -2560,9 +2636,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // 100 percent is full good, 0 is full bad. public void reportInetCondition(int networkType, int percentage) { - if (percentage > 50) return; // don't handle good network reports NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); - if (nai != null) reportBadNetwork(nai.network); + if (nai == null) return; + boolean isGood = percentage > 50; + // Revalidate if the app report does not match our current validated state. + if (isGood != nai.lastValidated) { + // Make the message logged by reportBadNetwork below less confusing. + if (DBG && isGood) log("reportInetCondition: type=" + networkType + " ok, revalidate"); + reportBadNetwork(nai.network); + } } public void reportBadNetwork(Network network) { @@ -4034,6 +4116,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { if (DBG) log(" accepting network in place of null"); } + unlinger(newNetwork); mNetworkForRequestId.put(nri.request.requestId, newNetwork); newNetwork.addRequest(nri.request); keep = true; @@ -4052,19 +4135,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Linger any networks that are no longer needed. for (NetworkAgentInfo nai : affectedNetworks) { - boolean teardown = !nai.isVPN() && nai.everValidated; - for (int i = 0; i < nai.networkRequests.size() && teardown; i++) { - NetworkRequest nr = nai.networkRequests.valueAt(i); - try { - if (isRequest(nr)) { - teardown = false; - } - } catch (Exception e) { - loge("Request " + nr + " not found in mNetworkRequests."); - loge(" it came from request list of " + nai.name()); - } - } - if (teardown) { + if (nai.everValidated && unneeded(nai)) { nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING); } else { @@ -4164,27 +4235,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) { for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { - if (!nai.created || nai.everValidated || nai.isVPN()) continue; - boolean reap = true; - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - // If this Network is already the highest scoring Network for a request, or if - // there is hope for it to become one if it validated, then don't reap it. - if (nri.isRequest && nai.satisfies(nri.request) && - (nai.networkRequests.get(nri.request.requestId) != null || - // Note that this catches two important cases: - // 1. Unvalidated cellular will not be reaped when unvalidated WiFi - // is currently satisfying the request. This is desirable when - // cellular ends up validating but WiFi does not. - // 2. Unvalidated WiFi will not be reaped when validated cellular - // is currently satsifying the request. This is desirable when - // WiFi ends up validating and out scoring cellular. - mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < - nai.getCurrentScoreAsValidated())) { - reap = false; - break; - } - } - if (reap) { + if (!nai.everValidated && unneeded(nai)) { if (DBG) log("Reaping " + nai.name()); teardownUnneededNetwork(nai); } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 891f572..170c4e2 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -1133,7 +1133,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void setIpForwardingEnabled(boolean enable) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("ipfwd", enable ? "enable" : "disable"); + mConnector.execute("ipfwd", enable ? "enable" : "disable", "tethering"); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } @@ -1259,6 +1259,27 @@ public class NetworkManagementService extends INetworkManagementService.Stub return filtered; } + private void modifyInterfaceForward(boolean add, String fromIface, String toIface) { + final Command cmd = new Command("ipfwd", add ? "add" : "remove", fromIface, toIface); + try { + mConnector.execute(cmd); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override + public void startInterfaceForwarding(String fromIface, String toIface) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + modifyInterfaceForward(true, fromIface, toIface); + } + + @Override + public void stopInterfaceForwarding(String fromIface, String toIface) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + modifyInterfaceForward(false, fromIface, toIface); + } + private void modifyNat(String action, String internalInterface, String externalInterface) throws SocketException { final Command cmd = new Command("nat", action, internalInterface, externalInterface); diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index fddb54e..d6abce9 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -55,7 +55,7 @@ public class NetworkTimeUpdateService { private static final int EVENT_AUTO_TIME_CHANGED = 1; private static final int EVENT_POLL_NETWORK_TIME = 2; - private static final int EVENT_NETWORK_CONNECTED = 3; + private static final int EVENT_NETWORK_CHANGED = 3; private static final String ACTION_POLL = "com.android.server.NetworkTimeUpdateService.action.POLL"; @@ -248,18 +248,8 @@ public class NetworkTimeUpdateService { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { - // There is connectivity - final ConnectivityManager connManager = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo netInfo = connManager.getActiveNetworkInfo(); - if (netInfo != null) { - // Verify that it's a WIFI connection - if (netInfo.getState() == NetworkInfo.State.CONNECTED && - (netInfo.getType() == ConnectivityManager.TYPE_WIFI || - netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) { - mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget(); - } - } + // Don't bother checking if we have connectivity, NtpTrustedTime does that for us. + mHandler.obtainMessage(EVENT_NETWORK_CHANGED).sendToTarget(); } } }; @@ -276,7 +266,7 @@ public class NetworkTimeUpdateService { switch (msg.what) { case EVENT_AUTO_TIME_CHANGED: case EVENT_POLL_NETWORK_TIME: - case EVENT_NETWORK_CONNECTED: + case EVENT_NETWORK_CHANGED: onPollNetworkTime(msg.what); break; } diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index 9d4cd99..e5ace1b 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -103,9 +103,19 @@ public class PersistentDataBlockService extends SystemService { @Override public void onStart() { enforceChecksumValidity(); + formatIfOemUnlockEnabled(); publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); } + private void formatIfOemUnlockEnabled() { + if (doGetOemUnlockEnabled()) { + synchronized (mLock) { + formatPartitionLocked(); + doSetOemUnlockEnabledLocked(true); + } + } + } + private void enforceOemUnlockPermission() { mContext.enforceCallingOrSelfPermission( Manifest.permission.OEM_UNLOCK_STATE, @@ -285,6 +295,28 @@ public class PersistentDataBlockService extends SystemService { } } + private boolean doGetOemUnlockEnabled() { + DataInputStream inputStream; + try { + inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Slog.e(TAG, "partition not available"); + return false; + } + + try { + synchronized (mLock) { + inputStream.skip(getBlockDeviceSize() - 1); + return inputStream.readByte() != 0; + } + } catch (IOException e) { + Slog.e(TAG, "unable to access persistent partition", e); + return false; + } finally { + IoUtils.closeQuietly(inputStream); + } + } + private native long nativeGetBlockDeviceSize(String path); private native int nativeWipe(String path); @@ -410,25 +442,7 @@ public class PersistentDataBlockService extends SystemService { @Override public boolean getOemUnlockEnabled() { enforceOemUnlockPermission(); - DataInputStream inputStream; - try { - inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); - } catch (FileNotFoundException e) { - Slog.e(TAG, "partition not available"); - return false; - } - - try { - synchronized (mLock) { - inputStream.skip(getBlockDeviceSize() - 1); - return inputStream.readByte() != 0; - } - } catch (IOException e) { - Slog.e(TAG, "unable to access persistent partition", e); - return false; - } finally { - IoUtils.closeQuietly(inputStream); - } + return doGetOemUnlockEnabled(); } @Override diff --git a/services/core/java/com/android/server/ShutdownActivity.java b/services/core/java/com/android/server/ShutdownActivity.java deleted file mode 100644 index 56172ed..0000000 --- a/services/core/java/com/android/server/ShutdownActivity.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2009 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; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.IPowerManager; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Slog; - -public class ShutdownActivity extends Activity { - - private static final String TAG = "ShutdownActivity"; - private boolean mReboot; - private boolean mConfirm; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - mReboot = Intent.ACTION_REBOOT.equals(intent.getAction()); - mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false); - Slog.i(TAG, "onCreate(): confirm=" + mConfirm); - - Thread thr = new Thread("ShutdownActivity") { - @Override - public void run() { - IPowerManager pm = IPowerManager.Stub.asInterface( - ServiceManager.getService(Context.POWER_SERVICE)); - try { - if (mReboot) { - pm.reboot(mConfirm, null, false); - } else { - pm.shutdown(mConfirm, false); - } - } catch (RemoteException e) { - } - } - }; - thr.start(); - finish(); - // Wait for us to tell the power manager to shutdown. - try { - thr.join(); - } catch (InterruptedException e) { - } - } -} diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index eb89f21..6ad128c 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -205,8 +205,8 @@ public class SystemConfig { } if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) { - throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + - ", expected 'permissions' or 'config'"); + throw new XmlPullParserException("Unexpected start tag in " + permFile + + ": found " + parser.getName() + ", expected 'permissions' or 'config'"); } while (true) { @@ -222,7 +222,7 @@ public class SystemConfig { int gid = android.os.Process.getGidForName(gidStr); mGlobalGids = appendInt(mGlobalGids, gid); } else { - Slog.w(TAG, "<group> without gid at " + Slog.w(TAG, "<group> without gid in " + permFile + " at " + parser.getPositionDescription()); } @@ -231,7 +231,7 @@ public class SystemConfig { } else if ("permission".equals(name) && !onlyFeatures) { String perm = parser.getAttributeValue(null, "name"); if (perm == null) { - Slog.w(TAG, "<permission> without name at " + Slog.w(TAG, "<permission> without name in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; @@ -242,14 +242,14 @@ public class SystemConfig { } else if ("assign-permission".equals(name) && !onlyFeatures) { String perm = parser.getAttributeValue(null, "name"); if (perm == null) { - Slog.w(TAG, "<assign-permission> without name at " + Slog.w(TAG, "<assign-permission> without name in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } String uidStr = parser.getAttributeValue(null, "uid"); if (uidStr == null) { - Slog.w(TAG, "<assign-permission> without uid at " + Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; @@ -257,7 +257,7 @@ public class SystemConfig { int uid = Process.getUidForName(uidStr); if (uid < 0) { Slog.w(TAG, "<assign-permission> with unknown uid \"" - + uidStr + "\" at " + + uidStr + " in " + permFile + " at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; @@ -275,10 +275,10 @@ public class SystemConfig { String lname = parser.getAttributeValue(null, "name"); String lfile = parser.getAttributeValue(null, "file"); if (lname == null) { - Slog.w(TAG, "<library> without name at " + Slog.w(TAG, "<library> without name in " + permFile + " at " + parser.getPositionDescription()); } else if (lfile == null) { - Slog.w(TAG, "<library> without file at " + Slog.w(TAG, "<library> without file in " + permFile + " at " + parser.getPositionDescription()); } else { //Log.i(TAG, "Got library " + lname + " in " + lfile); @@ -297,7 +297,7 @@ public class SystemConfig { allowed = !"true".equals(notLowRam); } if (fname == null) { - Slog.w(TAG, "<feature> without name at " + Slog.w(TAG, "<feature> without name in " + permFile + " at " + parser.getPositionDescription()); } else if (allowed) { //Log.i(TAG, "Got feature " + fname); @@ -311,7 +311,7 @@ public class SystemConfig { } else if ("unavailable-feature".equals(name)) { String fname = parser.getAttributeValue(null, "name"); if (fname == null) { - Slog.w(TAG, "<unavailable-feature> without name at " + Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at " + parser.getPositionDescription()); } else { mUnavailableFeatures.add(fname); @@ -322,7 +322,7 @@ public class SystemConfig { } else if ("allow-in-power-save".equals(name) && !onlyFeatures) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { - Slog.w(TAG, "<allow-in-power-save> without package at " + Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mAllowInPowerSave.add(pkgname); @@ -333,7 +333,7 @@ public class SystemConfig { } else if ("fixed-ime-app".equals(name) && !onlyFeatures) { String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { - Slog.w(TAG, "<fixed-ime-app> without package at " + Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mFixedImeApps.add(pkgname); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 72c2eaf..4fbf23b 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -259,7 +259,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { + newDefaultSubIdObj + " newDefaultPhoneId=" + newDefaultPhoneId); } - if(validatePhoneId(newDefaultPhoneId) && (newDefaultSubIdObj.equals(mDefaultSubId) + if(validatePhoneId(newDefaultPhoneId) && (!newDefaultSubIdObj.equals(mDefaultSubId) || (newDefaultPhoneId != mDefaultPhoneId))) { mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_DEFAULT_SUB, newDefaultPhoneId, 0, newDefaultSubIdObj)); @@ -338,7 +338,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void registerOnSubscriptionsChangedListener(String pkgForDebug, + public void addOnSubscriptionsChangedListener(String pkgForDebug, IOnSubscriptionsChangedListener callback) { int callerUid = UserHandle.getCallingUserId(); int myUid = UserHandle.myUserId(); @@ -393,7 +393,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void unregisterOnSubscriptionsChangedListener(String pkgForDebug, + public void removeOnSubscriptionsChangedListener(String pkgForDebug, IOnSubscriptionsChangedListener callback) { if (DBG) log("listen oscl: Unregister"); remove(callback.asBinder()); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index f02a815..aefbf60 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -19,11 +19,13 @@ package com.android.server.am; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import android.app.ActivityThread; import android.os.Build; import android.os.DeadObjectException; import android.os.Handler; @@ -34,6 +36,7 @@ import android.util.ArraySet; import com.android.internal.app.ProcessStats; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.TransferPipe; +import com.android.internal.util.FastPrintWriter; import com.android.server.am.ActivityManagerService.ItemMatcher; import com.android.server.am.ActivityManagerService.NeededUriGrants; @@ -141,6 +144,19 @@ public final class ActiveServices { final ArrayList<ServiceRecord> mDestroyingServices = new ArrayList<ServiceRecord>(); + /** Amount of time to allow a last ANR message to exist before freeing the memory. */ + static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours + + String mLastAnrDump; + + final Runnable mLastAnrDumpClearer = new Runnable() { + @Override public void run() { + synchronized (mAm) { + mLastAnrDump = null; + } + } + }; + static final class DelayingProcess extends ArrayList<ServiceRecord> { long timeoout; } @@ -751,6 +767,9 @@ public final class ActiveServices { } } + mAm.startAssociationLocked(callerApp.uid, callerApp.processName, + s.appInfo.uid, s.name, s.processName); + AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent); @@ -1445,10 +1464,10 @@ public final class ActiveServices { boolean created = false; try { - String nameTerm; - int lastPeriod = r.shortName.lastIndexOf('.'); - nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName; if (LOG_SERVICE_START_STOP) { + String nameTerm; + int lastPeriod = r.shortName.lastIndexOf('.'); + nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName; EventLogTags.writeAmCreateService( r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid); } @@ -1635,6 +1654,7 @@ public final class ActiveServices { } if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent); + r.destroyTime = SystemClock.uptimeMillis(); if (LOG_SERVICE_START_STOP) { EventLogTags.writeAmDestroyService( r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1); @@ -1673,6 +1693,7 @@ public final class ActiveServices { try { bumpServiceExecutingLocked(r, false, "destroy"); mDestroyingServices.add(r); + r.destroying = true; mAm.updateOomAdjLocked(r.app); r.app.thread.scheduleStopService(r); } catch (Exception e) { @@ -1746,6 +1767,8 @@ public final class ActiveServices { } } + mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name); + if (b.connections.size() == 0) { b.intent.apps.remove(b.client); } @@ -1792,7 +1815,7 @@ public final class ActiveServices { void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) { boolean inDestroying = mDestroyingServices.contains(r); if (r != null) { - if (type == 1) { + if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) { // This is a call from a service start... take care of // book-keeping. r.callStart = true; @@ -1841,6 +1864,20 @@ public final class ActiveServices { if (res == Service.START_STICKY_COMPATIBILITY) { r.callStart = false; } + } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) { + // This is the final call from destroying the service... we should + // actually be getting rid of the service at this point. Do some + // validation of its state, and ensure it will be fully removed. + if (!inDestroying) { + // Not sure what else to do with this... if it is not actually in the + // destroying list, we don't need to make sure to remove it from it. + Slog.wtfStack(TAG, "Service done with onDestroy, but not inDestroying: " + + r); + } else if (r.executeNesting != 1) { + Slog.wtfStack(TAG, "Service done with onDestroy, but executeNesting=" + + r.executeNesting + ": " + r); + r.executeNesting = 1; + } } final long origId = Binder.clearCallingIdentity(); serviceDoneExecutingLocked(r, inDestroying, inDestroying); @@ -2367,7 +2404,8 @@ public final class ActiveServices { if (proc.executingServices.size() == 0 || proc.thread == null) { return; } - long maxTime = SystemClock.uptimeMillis() - + final long now = SystemClock.uptimeMillis(); + final long maxTime = now - (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT); ServiceRecord timeout = null; long nextTime = 0; @@ -2383,7 +2421,15 @@ public final class ActiveServices { } if (timeout != null && mAm.mLruProcesses.contains(proc)) { Slog.w(TAG, "Timeout executing service: " + timeout); - anrMessage = "Executing service " + timeout.shortName; + StringWriter sw = new StringWriter(); + PrintWriter pw = new FastPrintWriter(sw, false, 1024); + pw.println(timeout); + timeout.dump(pw, " "); + pw.close(); + mLastAnrDump = sw.toString(); + mAm.mHandler.removeCallbacks(mLastAnrDumpClearer); + mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS); + anrMessage = "executing service " + timeout.shortName; } else { Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); @@ -2423,6 +2469,11 @@ public final class ActiveServices { pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)"); try { + if (mLastAnrDump != null) { + pw.println(" Last ANR service:"); + pw.print(mLastAnrDump); + pw.println(); + } int[] users = mAm.getUsersLocked(); for (int user : users) { ServiceMap smap = getServiceMap(user); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c0fc890..e8f3757 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -749,6 +749,38 @@ public final class ActivityManagerService extends ActivityManagerNative final ActiveServices mServices; + final static class Association { + final int mSourceUid; + final String mSourceProcess; + final int mTargetUid; + final ComponentName mTargetComponent; + final String mTargetProcess; + + int mCount; + long mTime; + + int mNesting; + long mStartTime; + + Association(int sourceUid, String sourceProcess, int targetUid, + ComponentName targetComponent, String targetProcess) { + mSourceUid = sourceUid; + mSourceProcess = sourceProcess; + mTargetUid = targetUid; + mTargetComponent = targetComponent; + mTargetProcess = targetProcess; + } + } + + /** + * When service association tracking is enabled, this is all of the associations we + * have seen. Mapping is target uid -> target component -> source uid -> source process name + * -> association data. + */ + final SparseArray<ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>>> + mAssociations = new SparseArray<>(); + boolean mTrackingAssociations; + /** * Backup/restore process management */ @@ -2071,8 +2103,10 @@ public final class ActivityManagerService extends ActivityManagerNative GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); + mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations")); + mConfiguration.setToDefaults(); - mConfiguration.setLocale(Locale.getDefault()); + mConfiguration.locale = Locale.getDefault(); mConfigurationSeq = mConfiguration.seq = 1; mProcessCpuTracker.init(); @@ -2324,7 +2358,7 @@ public final class ActivityManagerService extends ActivityManagerNative return mAppBindArgs; } - final void setFocusedActivityLocked(ActivityRecord r) { + final void setFocusedActivityLocked(ActivityRecord r, String reason) { if (mFocusedActivity != r) { if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivityLocked: r=" + r); mFocusedActivity = r; @@ -2333,12 +2367,14 @@ public final class ActivityManagerService extends ActivityManagerNative } else { finishRunningVoiceLocked(); } - mStackSupervisor.setFocusedStack(r); + mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity"); if (r != null) { mWindowManager.setFocusedApp(r.appToken, true); } applyUpdateLockStateLocked(r); } + EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, mCurrentUserId, + mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName); } final void clearFocusedActivity(ActivityRecord r) { @@ -2355,7 +2391,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (stack != null) { ActivityRecord r = stack.topRunningActivityLocked(null); if (r != null) { - setFocusedActivityLocked(r); + setFocusedActivityLocked(r, "setFocusedStack"); } } } @@ -2399,7 +2435,7 @@ public final class ActivityManagerService extends ActivityManagerNative mHandler.sendMessage(msg); } - private final int updateLruProcessInternalLocked(ProcessRecord app, long now, int index, + private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index, String what, Object obj, ProcessRecord srcApp) { app.lastActivityTime = now; @@ -2964,6 +3000,10 @@ public final class ActivityManagerService extends ActivityManagerNative instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi); } + app.gids = gids; + app.requiredAbi = requiredAbi; + app.instructionSet = instructionSet; + // Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. boolean isActivityProcess = (entryPoint == null); @@ -2994,7 +3034,11 @@ public final class ActivityManagerService extends ActivityManagerNative StringBuilder buf = mStringBuilder; buf.setLength(0); buf.append("Start proc "); + buf.append(startResult.pid); + buf.append(':'); buf.append(app.processName); + buf.append('/'); + UserHandle.formatUid(buf, uid); if (!isActivityProcess) { buf.append(" ["); buf.append(entryPoint); @@ -3006,23 +3050,6 @@ public final class ActivityManagerService extends ActivityManagerNative buf.append(" "); buf.append(hostingNameStr); } - buf.append(": pid="); - buf.append(startResult.pid); - buf.append(" uid="); - buf.append(uid); - buf.append(" gids={"); - if (gids != null) { - for (int gi=0; gi<gids.length; gi++) { - if (gi != 0) buf.append(", "); - buf.append(gids[gi]); - - } - } - buf.append("}"); - if (requiredAbi != null) { - buf.append(" abi="); - buf.append(requiredAbi); - } Slog.i(TAG, buf.toString()); app.setPid(startResult.pid); app.usingWrapper = startResult.usingWrapper; @@ -3082,7 +3109,7 @@ public final class ActivityManagerService extends ActivityManagerNative return intent; } - boolean startHomeActivityLocked(int userId) { + boolean startHomeActivityLocked(int userId, String reason) { if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopAction == null) { // We are running in factory test mode, but unable to find @@ -3104,7 +3131,7 @@ public final class ActivityManagerService extends ActivityManagerNative aInfo.applicationInfo.uid, true); if (app == null || app.instrumentationClass == null) { intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); - mStackSupervisor.startHomeActivity(intent, aInfo); + mStackSupervisor.startHomeActivity(intent, aInfo, reason); } } @@ -3619,6 +3646,10 @@ public final class ActivityManagerService extends ActivityManagerNative if (task == null) { throw new IllegalArgumentException("Task " + taskId + " not found."); } + if (task.getRootActivity() != null) { + moveTaskToFrontLocked(task.taskId, 0, null); + return ActivityManager.START_TASK_TO_FRONT; + } callingUid = task.mCallingUid; callingPackage = task.mCallingPackage; intent = task.intent; @@ -5289,7 +5320,6 @@ public final class ActivityManagerService extends ActivityManagerNative int callingPid = Binder.getCallingPid(); if (callingPid == Process.myPid()) { // Yeah, um, no. - Slog.w(TAG, "Can't addPackageDependency on system process"); return; } ProcessRecord proc; @@ -6173,7 +6203,7 @@ public final class ActivityManagerService extends ActivityManagerNative startProcessLocked(procs.get(ip), "on-hold", null); } } - + if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { // Start looking for apps that are abusing wake locks. Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); @@ -6313,7 +6343,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { - stack.activityDestroyedLocked(token); + stack.activityDestroyedLocked(token, "activityDestroyed"); } } } @@ -6424,7 +6454,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("File descriptors passed in options"); } } - + synchronized(this) { int callingUid = Binder.getCallingUid(); int origUserId = userId; @@ -6454,7 +6484,7 @@ public final class ActivityManagerService extends ActivityManagerNative return getIntentSenderLocked(type, packageName, callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags, options); - + } catch (RemoteException e) { throw new SecurityException(e); } @@ -8182,17 +8212,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private TaskRecord taskForIdLocked(int id) { - final TaskRecord task = recentTaskForIdLocked(id); - if (task != null) { - return task; - } - - // Don't give up. Sometimes it just hasn't made it to recents yet. - return mStackSupervisor.anyTaskForIdLocked(id); - } - - private TaskRecord recentTaskForIdLocked(int id) { + TaskRecord recentTaskForIdLocked(int id) { final int N = mRecentTasks.size(); for (int i=0; i<N; i++) { TaskRecord tr = mRecentTasks.get(i); @@ -8208,7 +8228,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "getTaskThumbnail()"); - TaskRecord tr = recentTaskForIdLocked(id); + TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id); if (tr != null) { return tr.getTaskThumbnailLocked(); } @@ -8453,7 +8473,7 @@ public final class ActivityManagerService extends ActivityManagerNative * @return Returns true if the given task was found and removed. */ private boolean removeTaskByIdLocked(int taskId, boolean killProcess) { - TaskRecord tr = taskForIdLocked(taskId); + TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId); if (tr != null) { tr.removeTaskActivitiesLocked(); cleanUpRemovedTaskLocked(tr, killProcess); @@ -8516,7 +8536,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (prev != null && prev.isRecentsActivity()) { task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE); } - mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options); + mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront"); } finally { Binder.restoreCallingIdentity(origId); } @@ -8529,7 +8549,7 @@ public final class ActivityManagerService extends ActivityManagerNative "moveTaskToBack()"); synchronized(this) { - TaskRecord tr = taskForIdLocked(taskId); + TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId); if (tr != null) { if (tr == mStackSupervisor.mLockTaskModeTask) { mStackSupervisor.showLockTaskToast(); @@ -8545,7 +8565,7 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); try { - stack.moveTaskToBackLocked(taskId, null); + stack.moveTaskToBackLocked(taskId); } finally { Binder.restoreCallingIdentity(origId); } @@ -8575,7 +8595,7 @@ public final class ActivityManagerService extends ActivityManagerNative mStackSupervisor.showLockTaskToast(); return false; } - return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId, null); + return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId); } } finally { Binder.restoreCallingIdentity(origId); @@ -8667,7 +8687,7 @@ public final class ActivityManagerService extends ActivityManagerNative try { if (DEBUG_STACK) Slog.d(TAG, "moveTaskToStack: moving task=" + taskId + " to stackId=" + stackId + " toTop=" + toTop); - mStackSupervisor.moveTaskToStack(taskId, stackId, toTop); + mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop); } finally { Binder.restoreCallingIdentity(ident); } @@ -8721,7 +8741,7 @@ public final class ActivityManagerService extends ActivityManagerNative long ident = Binder.clearCallingIdentity(); try { synchronized (this) { - TaskRecord tr = taskForIdLocked(taskId); + TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId); return tr != null && tr.stack != null && tr.stack.isHomeStack(); } } finally { @@ -8773,7 +8793,8 @@ public final class ActivityManagerService extends ActivityManagerNative || (task != mStackSupervisor.getFocusedStack().topTask()))) { throw new IllegalArgumentException("Invalid task, not in foreground"); } - mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated); + mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated, + "startLockTask"); } } } finally { @@ -8858,7 +8879,7 @@ public final class ActivityManagerService extends ActivityManagerNative Log.d(TAG, "stopLockTaskMode"); // Stop lock task synchronized (this) { - mStackSupervisor.setLockTaskModeLocked(null, false); + mStackSupervisor.setLockTaskModeLocked(null, false, "stopLockTask"); } } finally { Binder.restoreCallingIdentity(ident); @@ -9088,6 +9109,7 @@ public final class ActivityManagerService extends ActivityManagerNative } cpr.connections.add(conn); r.conProviders.add(conn); + startAssociationLocked(r.uid, r.processName, cpr.uid, cpr.name, cpr.info.processName); return conn; } cpr.addExternalProcessHandleLocked(externalProcessToken); @@ -9111,6 +9133,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (conn.stableCount == 0 && conn.unstableCount == 0) { cpr.connections.remove(conn); conn.client.conProviders.remove(conn); + stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name); return true; } return false; @@ -11177,9 +11200,6 @@ public final class ActivityManagerService extends ActivityManagerNative if (mRecentTasks == null) { mRecentTasks = mTaskPersister.restoreTasksLocked(); mTaskPersister.restoreTasksFromOtherDeviceLocked(); - if (!mRecentTasks.isEmpty()) { - mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks); - } cleanupRecentTasksLocked(UserHandle.USER_ALL); mTaskPersister.startPersisting(); } @@ -11196,8 +11216,7 @@ public final class ActivityManagerService extends ActivityManagerNative mDidUpdate = true; } writeLastDonePreBootReceivers(doneReceivers); - showBootMessage(mContext.getText( - R.string.android_upgrading_complete), + showBootMessage(mContext.getText(R.string.android_upgrading_complete), false); systemReady(goingCallback); } @@ -11320,7 +11339,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Start up initial activity. mBooting = true; - startHomeActivityLocked(mCurrentUserId); + startHomeActivityLocked(mCurrentUserId, "systemReady"); try { if (AppGlobals.getPackageManager().hasSystemUidErrors()) { @@ -12330,9 +12349,18 @@ public final class ActivityManagerService extends ActivityManagerNative dumpAll = true; } else if ("-c".equals(opt)) { dumpClient = true; + } else if ("-p".equals(opt)) { + if (opti < args.length) { + dumpPackage = args[opti]; + opti++; + } else { + pw.println("Error: -p option requires package argument"); + return; + } + dumpClient = true; } else if ("-h".equals(opt)) { pw.println("Activity manager dump options:"); - pw.println(" [-a] [-c] [-h] [cmd] ..."); + pw.println(" [-a] [-c] [-p package] [-h] [cmd] ..."); pw.println(" cmd may be one of:"); pw.println(" a[ctivities]: activity stack state"); pw.println(" r[recents]: recent activities state"); @@ -12343,17 +12371,21 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" prov[iders] [COMP_SPEC ...]: content provider state"); pw.println(" provider [COMP_SPEC]: provider client-side state"); pw.println(" s[ervices] [COMP_SPEC ...]: service state"); + pw.println(" as[sociations]: tracked app associations"); pw.println(" service [COMP_SPEC]: service client-side state"); pw.println(" package [PACKAGE_NAME]: all state related to given package"); pw.println(" all: dump all activities"); pw.println(" top: dump the top activity"); pw.println(" write: write all pending state to storage"); + pw.println(" track-associations: enable association tracking"); + pw.println(" untrack-associations: disable and clear association tracking"); pw.println(" cmd may also be a COMP_SPEC to dump activities."); pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),"); pw.println(" a partial substring in a component name, a"); pw.println(" hex object identifier."); pw.println(" -a: include all available server state."); pw.println(" -c: include client state."); + pw.println(" -p: limit output to given package."); return; } else { pw.println("Unknown argument: " + opt + "; use -h for help"); @@ -12368,11 +12400,11 @@ public final class ActivityManagerService extends ActivityManagerNative opti++; if ("activities".equals(cmd) || "a".equals(cmd)) { synchronized (this) { - dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, null); + dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage); } } else if ("recents".equals(cmd) || "r".equals(cmd)) { synchronized (this) { - dumpRecentsLocked(fd, pw, args, opti, true, null); + dumpRecentsLocked(fd, pw, args, opti, true, dumpPackage); } } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) { String[] newArgs; @@ -12381,14 +12413,14 @@ public final class ActivityManagerService extends ActivityManagerNative name = null; newArgs = EMPTY_STRING_ARRAY; } else { - name = args[opti]; + dumpPackage = args[opti]; opti++; newArgs = new String[args.length - opti]; if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } synchronized (this) { - dumpBroadcastsLocked(fd, pw, args, opti, true, name); + dumpBroadcastsLocked(fd, pw, args, opti, true, dumpPackage); } } else if ("intents".equals(cmd) || "i".equals(cmd)) { String[] newArgs; @@ -12397,14 +12429,14 @@ public final class ActivityManagerService extends ActivityManagerNative name = null; newArgs = EMPTY_STRING_ARRAY; } else { - name = args[opti]; + dumpPackage = args[opti]; opti++; newArgs = new String[args.length - opti]; if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } synchronized (this) { - dumpPendingIntentsLocked(fd, pw, args, opti, true, name); + dumpPendingIntentsLocked(fd, pw, args, opti, true, dumpPackage); } } else if ("processes".equals(cmd) || "p".equals(cmd)) { String[] newArgs; @@ -12413,14 +12445,14 @@ public final class ActivityManagerService extends ActivityManagerNative name = null; newArgs = EMPTY_STRING_ARRAY; } else { - name = args[opti]; + dumpPackage = args[opti]; opti++; newArgs = new String[args.length - opti]; if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } synchronized (this) { - dumpProcessesLocked(fd, pw, args, opti, true, name); + dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage); } } else if ("oom".equals(cmd) || "o".equals(cmd)) { synchronized (this) { @@ -12478,14 +12510,39 @@ public final class ActivityManagerService extends ActivityManagerNative opti = 0; more = true; } + } else if ("associations".equals(cmd) || "as".equals(cmd)) { + synchronized (this) { + dumpAssociationsLocked(fd, pw, args, opti, true, dumpClient, dumpPackage); + } } else if ("services".equals(cmd) || "s".equals(cmd)) { synchronized (this) { - mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null); + mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage); } } else if ("write".equals(cmd)) { mTaskPersister.flush(); pw.println("All tasks persisted."); return; + } else if ("track-associations".equals(cmd)) { + synchronized (this) { + if (!mTrackingAssociations) { + mTrackingAssociations = true; + pw.println("Association tracking started."); + } else { + pw.println("Association tracking already enabled."); + } + } + return; + } else if ("untrack-associations".equals(cmd)) { + synchronized (this) { + if (mTrackingAssociations) { + mTrackingAssociations = false; + mAssociations.clear(); + pw.println("Association tracking stopped."); + } else { + pw.println("Association tracking not running."); + } + } + return; } else { // Dumping a single activity? if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) { @@ -12527,6 +12584,13 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("-------------------------------------------------------------------------------"); } dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); + if (mAssociations.size() > 0) { + pw.println(); + if (dumpAll) { + pw.println("-------------------------------------------------------------------------------"); + } + dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); + } pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); @@ -12601,6 +12665,78 @@ public final class ActivityManagerService extends ActivityManagerNative } } + void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { + pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)"); + + int dumpUid = 0; + if (dumpPackage != null) { + IPackageManager pm = AppGlobals.getPackageManager(); + try { + dumpUid = pm.getPackageUid(dumpPackage, 0); + } catch (RemoteException e) { + } + } + + boolean printedAnything = false; + + final long now = SystemClock.uptimeMillis(); + + for (int i1=0, N1=mAssociations.size(); i1<N1; i1++) { + ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents + = mAssociations.valueAt(i1); + for (int i2=0, N2=targetComponents.size(); i2<N2; i2++) { + SparseArray<ArrayMap<String, Association>> sourceUids + = targetComponents.valueAt(i2); + for (int i3=0, N3=sourceUids.size(); i3<N3; i3++) { + ArrayMap<String, Association> sourceProcesses = sourceUids.valueAt(i3); + for (int i4=0, N4=sourceProcesses.size(); i4<N4; i4++) { + Association ass = sourceProcesses.valueAt(i4); + if (dumpPackage != null) { + if (!ass.mTargetComponent.getPackageName().equals(dumpPackage) + && UserHandle.getAppId(ass.mSourceUid) != dumpUid) { + continue; + } + } + printedAnything = true; + pw.print(" "); + pw.print(ass.mTargetProcess); + pw.print("/"); + UserHandle.formatUid(pw, ass.mTargetUid); + pw.print(" <- "); + pw.print(ass.mSourceProcess); + pw.print("/"); + UserHandle.formatUid(pw, ass.mSourceUid); + pw.println(); + pw.print(" via "); + pw.print(ass.mTargetComponent.flattenToShortString()); + pw.println(); + pw.print(" "); + long dur = ass.mTime; + if (ass.mNesting > 0) { + dur += now - ass.mStartTime; + } + TimeUtils.formatDuration(dur, pw); + pw.print(" ("); + pw.print(ass.mCount); + pw.println(" times)"); + if (ass.mNesting > 0) { + pw.print(" "); + pw.print(" Currently active: "); + TimeUtils.formatDuration(now - ass.mStartTime, pw); + pw.println(); + } + } + } + } + + } + + if (!printedAnything) { + pw.println(" (nothing)"); + } + } + void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; @@ -14697,7 +14833,9 @@ public final class ActivityManagerService extends ActivityManagerNative // In the protocol here, we don't expect the client to correctly // clean up this connection, we'll just remove it. cpr.connections.remove(i); - conn.client.conProviders.remove(conn); + if (conn.client.conProviders.remove(conn)) { + stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name); + } } } @@ -14783,6 +14921,8 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<app.conProviders.size(); i++) { ContentProviderConnection conn = app.conProviders.get(i); conn.provider.connections.remove(conn); + stopAssociationLocked(app.uid, app.processName, conn.provider.uid, + conn.provider.name); } app.conProviders.clear(); } @@ -15217,6 +15357,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void serviceDoneExecuting(IBinder token, int type, int startId, int res) { synchronized(this) { if (!(token instanceof ServiceRecord)) { + Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token); throw new IllegalArgumentException("Invalid service token"); } mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res); @@ -15848,6 +15989,9 @@ public final class ActivityManagerService extends ActivityManagerNative removeUriPermissionsForPackageLocked(ssp, userId, true); removeTasksByPackageNameLocked(ssp, userId); + if (userId == UserHandle.USER_OWNER) { + mTaskPersister.removeFromPackageCache(ssp); + } } } else { removeTasksByRemovedPackageComponentsLocked(ssp, userId); @@ -16468,6 +16612,7 @@ public final class ActivityManagerService extends ActivityManagerNative Configuration ci; synchronized(this) { ci = new Configuration(mConfiguration); + ci.userSetLocale = false; } return ci; } @@ -16725,6 +16870,69 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } + Association startAssociationLocked(int sourceUid, String sourceProcess, int targetUid, + ComponentName targetComponent, String targetProcess) { + if (!mTrackingAssociations) { + return null; + } + ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> components + = mAssociations.get(targetUid); + if (components == null) { + components = new ArrayMap<>(); + mAssociations.put(targetUid, components); + } + SparseArray<ArrayMap<String, Association>> sourceUids = components.get(targetComponent); + if (sourceUids == null) { + sourceUids = new SparseArray<>(); + components.put(targetComponent, sourceUids); + } + ArrayMap<String, Association> sourceProcesses = sourceUids.get(sourceUid); + if (sourceProcesses == null) { + sourceProcesses = new ArrayMap<>(); + sourceUids.put(sourceUid, sourceProcesses); + } + Association ass = sourceProcesses.get(sourceProcess); + if (ass == null) { + ass = new Association(sourceUid, sourceProcess, targetUid, targetComponent, + targetProcess); + sourceProcesses.put(sourceProcess, ass); + } + ass.mCount++; + ass.mNesting++; + if (ass.mNesting == 1) { + ass.mStartTime = SystemClock.uptimeMillis(); + } + return ass; + } + + void stopAssociationLocked(int sourceUid, String sourceProcess, int targetUid, + ComponentName targetComponent) { + if (!mTrackingAssociations) { + return; + } + ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> components + = mAssociations.get(targetUid); + if (components == null) { + return; + } + SparseArray<ArrayMap<String, Association>> sourceUids = components.get(targetComponent); + if (sourceUids == null) { + return; + } + ArrayMap<String, Association> sourceProcesses = sourceUids.get(sourceUid); + if (sourceProcesses == null) { + return; + } + Association ass = sourceProcesses.get(sourceProcess); + if (ass == null || ass.mNesting <= 0) { + return; + } + ass.mNesting--; + if (ass.mNesting == 0) { + ass.mTime += SystemClock.uptimeMillis() - ass.mStartTime; + } + } + private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP, boolean doingAll, long now) { if (mAdjSeq == app.adjSeq) { @@ -18678,7 +18886,7 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } - mStackSupervisor.setLockTaskModeLocked(null, false); + mStackSupervisor.setLockTaskModeLocked(null, false, "startUser"); final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId); if (userInfo == null) { @@ -18935,7 +19143,7 @@ public final class ActivityManagerService extends ActivityManagerNative void moveUserToForeground(UserStartedState uss, int oldUserId, int newUserId) { boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss); if (homeInFront) { - startHomeActivityLocked(newUserId); + startHomeActivityLocked(newUserId, "moveUserToFroreground"); } else { mStackSupervisor.resumeTopActivitiesLocked(); } @@ -19457,20 +19665,8 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void moveToFront() { checkCaller(); - - final TaskRecord tr; - synchronized (ActivityManagerService.this) { - tr = recentTaskForIdLocked(mTaskId); - if (tr == null) { - throw new IllegalArgumentException("Unable to find task ID " + mTaskId); - } - if (tr.getRootActivity() != null) { - moveTaskToFrontLocked(tr.taskId, 0, null); - return; - } - } - - startActivityFromRecentsInner(tr.taskId, null); + // Will bring task to front if it already has a root activity. + startActivityFromRecentsInner(mTaskId, null); } @Override diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 912ca62..b1b2a5c 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -81,7 +81,7 @@ final class ActivityRecord { private static final String ATTR_USERID = "user_id"; private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle"; private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid"; - private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package"; + static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package"; private static final String ATTR_RESOLVEDTYPE = "resolved_type"; private static final String ATTR_COMPONENTSPECIFIED = "component_specified"; static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_"; @@ -90,7 +90,7 @@ final class ActivityRecord { final IApplicationToken.Stub appToken; // window manager token final ActivityInfo info; // all about me final ApplicationInfo appInfo; // information about activity's app - final int launchedFromUid; // always the uid who started the activity. + int launchedFromUid; // always the uid who started the activity. final String launchedFromPackage; // always the package who started the activity. final int userId; // Which user is this running for? final Intent intent; // the original intent that generated us @@ -516,7 +516,7 @@ final class ActivityRecord { void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) { if (task != null && task.removeActivity(this)) { if (task != newTask) { - task.stack.removeTask(task); + task.stack.removeTask(task, "setTask"); } else { Slog.d(TAG, "!!! REMOVE THIS LOG !!! setTask: nearly removed stack=" + (newTask == null ? null : newTask.stack)); @@ -1143,8 +1143,8 @@ final class ActivityRecord { } } - static ActivityRecord restoreFromXml(XmlPullParser in, - ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException { + static ActivityRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor) + throws IOException, XmlPullParserException { Intent intent = null; PersistableBundle persistentState = null; int launchedFromUid = 0; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 9870a1a..7908da0 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -291,7 +291,7 @@ final class ActivityStack { // so we need to be conservative and assume it isn't. Slog.w(TAG, "Activity destroy timeout for " + r); synchronized (mService) { - activityDestroyedLocked(r != null ? r.appToken : null); + activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout"); } } break; case STOP_TIMEOUT_MSG: { @@ -473,10 +473,10 @@ final class ActivityStack { mActivityContainer.mActivityDisplay.mDisplayId == Display.DEFAULT_DISPLAY; } - final void moveToFront() { + final void moveToFront(String reason) { if (isAttached()) { if (isOnHomeDisplay()) { - mStackSupervisor.moveHomeStack(isHomeStack()); + mStackSupervisor.moveHomeStack(isHomeStack(), reason); } mStacks.remove(this); mStacks.add(this); @@ -1478,7 +1478,7 @@ final class ActivityStack { cancelInitializingActivities(); // Find the first activity that is not finishing. - ActivityRecord next = topRunningActivityLocked(null); + final ActivityRecord next = topRunningActivityLocked(null); // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. @@ -1496,7 +1496,7 @@ final class ActivityStack { final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo(); return isOnHomeDisplay() && - mStackSupervisor.resumeHomeStackTask(returnTaskType, prev); + mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "noMoreActivities"); } next.delayedResume = false; @@ -1532,7 +1532,7 @@ final class ActivityStack { final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo(); return isOnHomeDisplay() && - mStackSupervisor.resumeHomeStackTask(returnTaskType, prev); + mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "prevFinished"); } } @@ -1817,9 +1817,8 @@ final class ActivityStack { next.app.thread.scheduleNewIntent(next.newIntents, next.appToken); } - EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, - next.userId, System.identityHashCode(next), - next.task.taskId, next.shortComponentName); + EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, + System.identityHashCode(next), next.task.taskId, next.shortComponentName); next.sleeping = false; mService.showAskCompatModeDialogLocked(next); @@ -2466,18 +2465,19 @@ final class ActivityStack { r.addResultLocked(null, resultWho, requestCode, resultCode, data); } - private void adjustFocusedActivityLocked(ActivityRecord r) { + private void adjustFocusedActivityLocked(ActivityRecord r, String reason) { if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) { ActivityRecord next = topRunningActivityLocked(null); if (next != r) { final TaskRecord task = r.task; if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) { - mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo()); + mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), + reason + " adjustFocus"); } } ActivityRecord top = mStackSupervisor.topRunningActivityLocked(); if (top != null) { - mService.setFocusedActivityLocked(top); + mService.setFocusedActivityLocked(top, reason + " adjustTopFocus"); } } } @@ -2501,7 +2501,7 @@ final class ActivityStack { } if (r.app != null && r.app.thread != null) { - adjustFocusedActivityLocked(r); + adjustFocusedActivityLocked(r, "stopActivity"); r.resumeKeyDispatchingLocked(); try { r.stopped = false; @@ -2576,7 +2576,7 @@ final class ActivityStack { if (r != null && r.app == app) { // If the top running activity is from this crashing // process, then terminate it to avoid getting in a loop. - Slog.w(TAG, " Force finishing activity " + Slog.w(TAG, " Force finishing activity 1 " + r.intent.getComponent().flattenToShortString()); int taskNdx = mTaskHistory.indexOf(r.task); int activityNdx = r.task.mActivities.indexOf(r); @@ -2600,7 +2600,7 @@ final class ActivityStack { || r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) { if (!r.isHomeActivity() || mService.mHomeProcess != r.app) { - Slog.w(TAG, " Force finishing activity " + Slog.w(TAG, " Force finishing activity 2 " + r.intent.getComponent().flattenToShortString()); finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false); } @@ -2705,7 +2705,7 @@ final class ActivityStack { r.pauseKeyDispatchingLocked(); - adjustFocusedActivityLocked(r); + adjustFocusedActivityLocked(r, "finishActivity"); finishActivityResultsLocked(r, resultCode, resultData); @@ -3008,7 +3008,7 @@ final class ActivityStack { r.finishLaunchTickingLocked(); } - private void removeActivityFromHistoryLocked(ActivityRecord r) { + private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) { mStackSupervisor.removeChildActivityContainers(r); finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null); r.makeFinishing(); @@ -3033,9 +3033,9 @@ final class ActivityStack { "removeActivityFromHistoryLocked: last activity removed from " + this); if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.isOverHomeStack()) { - mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo()); + mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason); } - removeTask(task); + removeTask(task, reason); } cleanUpActivityServicesLocked(r); r.removeUriPermissionsLocked(); @@ -3200,7 +3200,7 @@ final class ActivityStack { // up. //Slog.w(TAG, "Exception thrown during finish", e); if (r.finishing) { - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy"); removedFromHistory = true; skipDestroy = true; } @@ -3230,7 +3230,7 @@ final class ActivityStack { } else { // remove this record from the history. if (r.finishing) { - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, reason + " hadNoApp"); removedFromHistory = true; } else { if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (no app)"); @@ -3249,7 +3249,7 @@ final class ActivityStack { return removedFromHistory; } - final void activityDestroyedLocked(IBinder token) { + final void activityDestroyedLocked(IBinder token, String reason) { final long origId = Binder.clearCallingIdentity(); try { ActivityRecord r = ActivityRecord.forToken(token); @@ -3261,7 +3261,7 @@ final class ActivityStack { if (isInStackLocked(token) != null) { if (r.state == ActivityState.DESTROYING) { cleanUpActivityLocked(r, true, false); - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, reason); } } mStackSupervisor.resumeTopActivitiesLocked(); @@ -3397,7 +3397,7 @@ final class ActivityStack { mService.updateUsageStats(r, false); } } - removeActivityFromHistoryLocked(r); + removeActivityFromHistoryLocked(r, "appDied"); } else { // We have the current state for this activity, so @@ -3466,15 +3466,16 @@ final class ActivityStack { } } - final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) { + final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options, + String reason) { if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); final int numTasks = mTaskHistory.size(); final int index = mTaskHistory.indexOf(tr); if (numTasks == 0 || index < 0) { // nothing to do! - if (reason != null && - (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + if (source != null && + (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { ActivityOptions.abort(options); } else { updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options); @@ -3485,11 +3486,11 @@ final class ActivityStack { // Shift all activities with this task up to the top // of the stack, keeping them in the same internal order. insertTaskAtTop(tr); - moveToFront(); + moveToFront(reason); if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr); - if (reason != null && - (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + if (source != null && + (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); ActivityRecord r = topRunningActivityLocked(null); if (r != null) { @@ -3519,7 +3520,7 @@ final class ActivityStack { * @param taskId The taskId to collect and move to the bottom. * @return Returns true if the move completed, false if not. */ - final boolean moveTaskToBackLocked(int taskId, ActivityRecord reason) { + final boolean moveTaskToBackLocked(int taskId) { final TaskRecord tr = taskForIdLocked(taskId); if (tr == null) { Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId); @@ -3574,16 +3575,7 @@ final class ActivityStack { } } - if (reason != null && - (reason.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); - ActivityRecord r = topRunningActivityLocked(null); - if (r != null) { - mNoAnimActivities.add(r); - } - } else { - mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false); - } + mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false); mWindowManager.moveTaskToBottom(taskId); if (VALIDATE_TOKENS) { @@ -3598,7 +3590,7 @@ final class ActivityStack { } final int taskToReturnTo = tr.getTaskToReturnTo(); tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); - return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null); + return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null, "moveTaskToBack"); } mStackSupervisor.resumeTopActivitiesLocked(); @@ -3850,7 +3842,7 @@ final class ActivityStack { } } didSomething = true; - Slog.i(TAG, " Force finishing activity " + r); + Slog.i(TAG, " Force finishing activity 3 " + r); if (samePackage) { if (r.app != null) { r.app.removed = true; @@ -3960,7 +3952,7 @@ final class ActivityStack { for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); if (r.app == app) { - Slog.w(TAG, " Force finishing activity " + Slog.w(TAG, " Force finishing activity 4 " + r.intent.getComponent().flattenToShortString()); // Force the destroy to skip right to removal. r.app = null; @@ -4040,7 +4032,7 @@ final class ActivityStack { return starting; } - void removeTask(TaskRecord task) { + void removeTask(TaskRecord task, String reason) { mStackSupervisor.endLockTaskModeIfTaskEnding(task); mWindowManager.removeTask(task.taskId); final ActivityRecord r = mResumedActivity; @@ -4078,7 +4070,7 @@ final class ActivityStack { if (mTaskHistory.isEmpty()) { if (DEBUG_STACK) Slog.i(TAG, "removeTask: moving to back stack=" + this); if (isOnHomeDisplay()) { - mStackSupervisor.moveHomeStack(!isHomeStack()); + mStackSupervisor.moveHomeStack(!isHomeStack(), reason + " leftTaskHistoryEmpty"); } if (mStacks != null) { mStacks.remove(this); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index df5b3c5..32787d8 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -24,6 +24,7 @@ import static com.android.server.am.ActivityManagerService.localLOGV; import static com.android.server.am.ActivityManagerService.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerService.DEBUG_FOCUS; import static com.android.server.am.ActivityManagerService.DEBUG_PAUSE; +import static com.android.server.am.ActivityManagerService.DEBUG_RECENTS; import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS; import static com.android.server.am.ActivityManagerService.DEBUG_STACK; import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH; @@ -397,9 +398,9 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - void moveHomeStack(boolean toFront) { + void moveHomeStack(boolean toFront, String reason) { ArrayList<ActivityStack> stacks = mHomeStack.mStacks; - int topNdx = stacks.size() - 1; + final int topNdx = stacks.size() - 1; if (topNdx <= 0) { return; } @@ -413,18 +414,28 @@ public final class ActivityStackSupervisor implements DisplayListener { if (DEBUG_STACK) Slog.d(TAG, "moveHomeTask: topStack old=" + topStack + " new=" + mFocusedStack); } + EventLog.writeEvent(EventLogTags.AM_HOME_STACK_MOVED, + mCurrentUser, toFront ? 1 : 0, stacks.get(topNdx).getStackId(), + mFocusedStack == null ? -1 : mFocusedStack.getStackId(), reason); + + if (mService.mBooting || !mService.mBooted) { + final ActivityRecord r = topRunningActivityLocked(); + if (r != null && r.idle) { + checkFinishBootingLocked(); + } + } } - void moveHomeStackTaskToTop(int homeStackTaskType) { + void moveHomeStackTaskToTop(int homeStackTaskType, String reason) { if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) { mWindowManager.showRecentApps(); return; } - moveHomeStack(true); + moveHomeStack(true, reason); mHomeStack.moveHomeStackTaskToTop(homeStackTaskType); } - boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) { + boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev, String reason) { if (!mService.mBooting && !mService.mBooted) { // Not ready yet! return false; @@ -434,7 +445,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mWindowManager.showRecentApps(); return false; } - moveHomeStackTaskToTop(homeStackTaskType); + moveHomeStackTaskToTop(homeStackTaskType, reason); if (prev != null) { prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); } @@ -442,10 +453,10 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityRecord r = mHomeStack.topRunningActivityLocked(null); // if (r != null && (r.isHomeActivity() || r.isRecentsActivity())) { if (r != null && r.isHomeActivity()) { - mService.setFocusedActivityLocked(r); + mService.setFocusedActivityLocked(r, reason); return resumeTopActivitiesLocked(mHomeStack, prev, null); } - return mService.startHomeActivityLocked(mCurrentUser); + return mService.startHomeActivityLocked(mCurrentUser, reason); } TaskRecord anyTaskForIdLocked(int id) { @@ -460,7 +471,21 @@ public final class ActivityStackSupervisor implements DisplayListener { } } } - return null; + + // Don't give up! Look in recents. + if (DEBUG_RECENTS) Slog.v(TAG, "Looking for task id=" + id + " in recents"); + TaskRecord task = mService.recentTaskForIdLocked(id); + if (task == null) { + if (DEBUG_RECENTS) Slog.d(TAG, "\tDidn't find task id=" + id + " in recents"); + return null; + } + + if (!restoreRecentTaskLocked(task)) { + if (DEBUG_RECENTS) Slog.w(TAG, "Couldn't restore task id=" + id + " found in recents"); + return null; + } + if (DEBUG_RECENTS) Slog.w(TAG, "Restored task id=" + id + " from in recents"); + return task; } ActivityRecord isInAnyStackLocked(IBinder token) { @@ -803,8 +828,8 @@ public final class ActivityStackSupervisor implements DisplayListener { return aInfo; } - void startHomeActivity(Intent intent, ActivityInfo aInfo) { - moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE); + void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) { + moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason); startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0, 0, 0, null, false, null, null, null); } @@ -1556,7 +1581,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return mHomeStack; } - void setFocusedStack(ActivityRecord r) { + void setFocusedStack(ActivityRecord r, String reason) { if (r != null) { final TaskRecord task = r.task; boolean isHomeActivity = !r.isApplicationActivity(); @@ -1567,7 +1592,7 @@ public final class ActivityStackSupervisor implements DisplayListener { final ActivityRecord parent = task.stack.mActivityContainer.mParentActivity; isHomeActivity = parent != null && parent.isHomeActivity(); } - moveHomeStack(isHomeActivity); + moveHomeStack(isHomeActivity, reason); } } @@ -1815,7 +1840,7 @@ public final class ActivityStackSupervisor implements DisplayListener { targetStack.mLastPausedActivity = null; if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack + " from " + intentActivity); - targetStack.moveToFront(); + targetStack.moveToFront("intentActivityFound"); if (intentActivity.task.intent == null) { // This task was started because of movement of // the activity based on affinity... now that we @@ -1844,7 +1869,8 @@ public final class ActivityStackSupervisor implements DisplayListener { intentActivity.setTaskToAffiliateWith(sourceRecord.task); } movedHome = true; - targetStack.moveTaskToFrontLocked(intentActivity.task, r, options); + targetStack.moveTaskToFrontLocked(intentActivity.task, r, options, + "bringingFoundTaskToFront"); if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) { @@ -2042,7 +2068,7 @@ public final class ActivityStackSupervisor implements DisplayListener { newTask = true; targetStack = adjustStackFocus(r, newTask); if (!launchTaskBehind) { - targetStack.moveToFront(); + targetStack.moveToFront("startingNewTask"); } if (reuseTask == null) { r.setTask(targetStack.createTaskRecord(getNextTaskId(), @@ -2071,10 +2097,10 @@ public final class ActivityStackSupervisor implements DisplayListener { return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; } targetStack = sourceTask.stack; - targetStack.moveToFront(); + targetStack.moveToFront("sourceStackToFront"); final TaskRecord topTask = targetStack.topTask(); if (topTask != sourceTask) { - targetStack.moveTaskToFrontLocked(sourceTask, r, options); + targetStack.moveTaskToFrontLocked(sourceTask, r, options, "sourceTaskToFront"); } if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { // In this case, we are adding the activity to an existing @@ -2128,7 +2154,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; } targetStack = inTask.stack; - targetStack.moveTaskToFrontLocked(inTask, r, options); + targetStack.moveTaskToFrontLocked(inTask, r, options, "inTaskToFront"); // Check whether we should actually launch the new activity in to the task, // or just reuse the current activity on top. @@ -2164,7 +2190,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // of a new task... just put it in the top task, though these days // this case should never happen. targetStack = adjustStackFocus(r, newTask); - targetStack.moveToFront(); + targetStack.moveToFront("addingToTopTask"); ActivityRecord prev = targetStack.topActivity(); r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, null, null, true), null); @@ -2187,7 +2213,7 @@ public final class ActivityStackSupervisor implements DisplayListener { targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options); if (!launchTaskBehind) { // Don't set focus on an activity that's going to the back. - mService.setFocusedActivityLocked(r); + mService.setFocusedActivityLocked(r, "startedActivity"); } return ActivityManager.START_SUCCESS; } @@ -2220,6 +2246,24 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + /** + * Called when the frontmost task is idle. + * @return the state of mService.mBooting before this was called. + */ + private boolean checkFinishBootingLocked() { + final boolean booting = mService.mBooting; + boolean enableScreen = false; + mService.mBooting = false; + if (!mService.mBooted) { + mService.mBooted = true; + enableScreen = true; + } + if (booting || enableScreen) { + mService.postFinishBooting(booting, enableScreen); + } + return booting; + } + // Checked. final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout, Configuration config) { @@ -2231,7 +2275,6 @@ public final class ActivityStackSupervisor implements DisplayListener { int NS = 0; int NF = 0; boolean booting = false; - boolean enableScreen = false; boolean activityRemoved = false; ActivityRecord r = ActivityRecord.forToken(token); @@ -2259,12 +2302,7 @@ public final class ActivityStackSupervisor implements DisplayListener { //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout); if (isFrontStack(r.task.stack) || fromTimeout) { - booting = mService.mBooting; - mService.mBooting = false; - if (!mService.mBooted) { - mService.mBooted = true; - enableScreen = true; - } + booting = checkFinishBootingLocked(); } } @@ -2337,10 +2375,6 @@ public final class ActivityStackSupervisor implements DisplayListener { //dump(); //mWindowManager.dump(); - if (booting || enableScreen) { - mService.postFinishBooting(booting, enableScreen); - } - if (activityRemoved) { resumeTopActivitiesLocked(); } @@ -2476,7 +2510,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options) { + void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options, String reason) { if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { mUserLeaving = true; } @@ -2485,7 +2519,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // we'll just indicate that this task returns to the home task. task.setTaskToReturnTo(HOME_ACTIVITY_TYPE); } - task.stack.moveTaskToFrontLocked(task, null, options); + task.stack.moveTaskToFrontLocked(task, null, options, reason); if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack=" + task.stack); } @@ -2586,26 +2620,58 @@ public final class ActivityStackSupervisor implements DisplayListener { return mLastStackId; } - void createStackForRestoredTaskHistory(ArrayList<TaskRecord> tasks) { - int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY); - final ActivityStack stack = getStack(stackId); - for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { - final TaskRecord task = tasks.get(taskNdx); - stack.addTask(task, false, false); - final int taskId = task.taskId; - final ArrayList<ActivityRecord> activities = task.mActivities; - for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - mWindowManager.addAppToken(0, r.appToken, taskId, stackId, - r.info.screenOrientation, r.fullscreen, - (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, - r.userId, r.info.configChanges, task.voiceSession != null, - r.mLaunchTaskBehind); + private boolean restoreRecentTaskLocked(TaskRecord task) { + ActivityStack stack = null; + // Determine stack to restore task to. + if (mLeanbackOnlyDevice) { + // There is only one stack for lean back devices. + stack = mHomeStack; + } else { + // Look for the top stack on the home display that isn't the home stack. + final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks; + for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack tmpStack = homeDisplayStacks.get(stackNdx); + if (!tmpStack.isHomeStack()) { + stack = tmpStack; + break; + } } } + + if (stack == null) { + // We couldn't find a stack to restore the task to. Possible if are restoring recents + // before an application stack is created...Go ahead and create one on the default + // display. + stack = getStack(createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY)); + // Restore home stack to top. + moveHomeStack(true, "restoreRecentTask"); + if (DEBUG_RECENTS) + Slog.v(TAG, "Created stack=" + stack + " for recents restoration."); + } + + if (stack == null) { + // What does this mean??? Not sure how we would get here... + if (DEBUG_RECENTS) + Slog.v(TAG, "Unable to find/create stack to restore recent task=" + task); + return false; + } + + stack.addTask(task, false, false); + if (DEBUG_RECENTS) + Slog.v(TAG, "Added restored task=" + task + " to stack=" + stack); + final ArrayList<ActivityRecord> activities = task.mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + mWindowManager.addAppToken(0, r.appToken, task.taskId, stack.mStackId, + r.info.screenOrientation, r.fullscreen, + (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, + r.userId, r.info.configChanges, task.voiceSession != null, + r.mLaunchTaskBehind); + } + return true; } - void moveTaskToStack(int taskId, int stackId, boolean toTop) { + void moveTaskToStackLocked(int taskId, int stackId, boolean toTop) { final TaskRecord task = anyTaskForIdLocked(taskId); if (task == null) { return; @@ -2615,7 +2681,7 @@ public final class ActivityStackSupervisor implements DisplayListener { Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId); return; } - task.stack.removeTask(task); + task.stack.removeTask(task, "moveTaskToStack"); stack.addTask(task, toTop, true); mWindowManager.addTask(taskId, stackId, toTop); resumeTopActivitiesLocked(); @@ -2981,14 +3047,14 @@ public final class ActivityStackSupervisor implements DisplayListener { } final boolean homeInFront = stack.isHomeStack(); if (stack.isOnHomeDisplay()) { - moveHomeStack(homeInFront); + moveHomeStack(homeInFront, "switchUserOnHomeDisplay"); TaskRecord task = stack.topTask(); if (task != null) { mWindowManager.moveTaskToTop(task.taskId); } } else { // Stack was moved to another display while user was swapped out. - resumeHomeStackTask(HOME_ACTIVITY_TYPE, null); + resumeHomeStackTask(HOME_ACTIVITY_TYPE, null, "switchUserOnOtherDisplay"); } return homeInFront; } @@ -3389,7 +3455,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mLockTaskNotify.showToast(mLockTaskIsLocked); } - void setLockTaskModeLocked(TaskRecord task, boolean isLocked) { + void setLockTaskModeLocked(TaskRecord task, boolean isLocked, String reason) { if (task == null) { // Take out of lock task mode if necessary if (mLockTaskModeTask != null) { @@ -3406,7 +3472,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return; } mLockTaskModeTask = task; - findTaskToMoveToFrontLocked(task, 0, null); + findTaskToMoveToFrontLocked(task, 0, null, reason); resumeTopActivitiesLocked(); final Message lockTaskMsg = Message.obtain(); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 0dc163b..d1682b8 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -108,50 +108,46 @@ final class CoreSettingsObserver extends ContentObserver { for (Map.Entry<String, Class<?>> entry : map.entrySet()) { String setting = entry.getKey(); Class<?> type = entry.getValue(); - try { - if (type == String.class) { - final String value; - if (map == sSecureSettingToTypeMap) { - value = Settings.Secure.getString(context.getContentResolver(), setting); - } else if (map == sSystemSettingToTypeMap) { - value = Settings.System.getString(context.getContentResolver(), setting); - } else { - value = Settings.Global.getString(context.getContentResolver(), setting); - } - snapshot.putString(setting, value); - } else if (type == int.class) { - final int value; - if (map == sSecureSettingToTypeMap) { - value = Settings.Secure.getInt(context.getContentResolver(), setting); - } else if (map == sSystemSettingToTypeMap) { - value = Settings.System.getInt(context.getContentResolver(), setting); - } else { - value = Settings.Global.getInt(context.getContentResolver(), setting); - } - snapshot.putInt(setting, value); - } else if (type == float.class) { - final float value; - if (map == sSecureSettingToTypeMap) { - value = Settings.Secure.getFloat(context.getContentResolver(), setting); - } else if (map == sSystemSettingToTypeMap) { - value = Settings.System.getFloat(context.getContentResolver(), setting); - } else { - value = Settings.Global.getFloat(context.getContentResolver(), setting); - } - snapshot.putFloat(setting, value); - } else if (type == long.class) { - final long value; - if (map == sSecureSettingToTypeMap) { - value = Settings.Secure.getLong(context.getContentResolver(), setting); - } else if (map == sSystemSettingToTypeMap) { - value = Settings.System.getLong(context.getContentResolver(), setting); - } else { - value = Settings.Global.getLong(context.getContentResolver(), setting); - } - snapshot.putLong(setting, value); + if (type == String.class) { + final String value; + if (map == sSecureSettingToTypeMap) { + value = Settings.Secure.getString(context.getContentResolver(), setting); + } else if (map == sSystemSettingToTypeMap) { + value = Settings.System.getString(context.getContentResolver(), setting); + } else { + value = Settings.Global.getString(context.getContentResolver(), setting); } - } catch (SettingNotFoundException snfe) { - Log.w(LOG_TAG, "Cannot find setting \"" + setting + "\"", snfe); + snapshot.putString(setting, value); + } else if (type == int.class) { + final int value; + if (map == sSecureSettingToTypeMap) { + value = Settings.Secure.getInt(context.getContentResolver(), setting, 0); + } else if (map == sSystemSettingToTypeMap) { + value = Settings.System.getInt(context.getContentResolver(), setting, 0); + } else { + value = Settings.Global.getInt(context.getContentResolver(), setting, 0); + } + snapshot.putInt(setting, value); + } else if (type == float.class) { + final float value; + if (map == sSecureSettingToTypeMap) { + value = Settings.Secure.getFloat(context.getContentResolver(), setting, 0); + } else if (map == sSystemSettingToTypeMap) { + value = Settings.System.getFloat(context.getContentResolver(), setting, 0); + } else { + value = Settings.Global.getFloat(context.getContentResolver(), setting, 0); + } + snapshot.putFloat(setting, value); + } else if (type == long.class) { + final long value; + if (map == sSecureSettingToTypeMap) { + value = Settings.Secure.getLong(context.getContentResolver(), setting, 0); + } else if (map == sSystemSettingToTypeMap) { + value = Settings.System.getLong(context.getContentResolver(), setting, 0); + } else { + value = Settings.Global.getLong(context.getContentResolver(), setting, 0); + } + snapshot.putLong(setting, value); } } } diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index d3cc56b..c376744 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -89,3 +89,9 @@ option java_package com.android.server.am # Activity fully drawn time 30042 am_activity_fully_drawn_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3) + +# Activity focused +30043 am_focused_activity (User|1|5),(Component Name|3) + +# Home Stack brought to front or rear +30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5),(Reason|3) diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index ae4af5f..c380160 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -235,15 +235,16 @@ final class ProcessList { Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs); } - if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { - // Increase the high min-free levels for cached processes for 64-bit - mOomMinFreeHigh[4] = (mOomMinFreeHigh[4]*3)/2; - mOomMinFreeHigh[5] = (mOomMinFreeHigh[5]*7)/4; - } + final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0; for (int i=0; i<mOomAdj.length; i++) { int low = mOomMinFreeLow[i]; int high = mOomMinFreeHigh[i]; + if (is64bit) { + // Increase the high min-free levels for cached processes for 64-bit + if (i == 4) high = (high*3)/2; + else if (i == 5) high = (high*7)/4; + } mOomMinFree[i] = (int)(low + ((high-low)*scale)); } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 7c48f3e..a6c616a 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -64,6 +64,9 @@ final class ProcessRecord { ProcessStats.ProcessState baseProcessTracker; BatteryStatsImpl.Uid.Proc curProcBatteryStats; int pid; // The process of this application; 0 if none + int[] gids; // The gids this process was launched with + String requiredAbi; // The ABI this process was launched with + String instructionSet; // The instruction set this process was launched with boolean starting; // True if the process is being started long lastActivityTime; // For managing the LRU list long lastPssTime; // Last time we retrieved PSS data @@ -183,7 +186,17 @@ final class ProcessRecord { if (uid != info.uid) { pw.print(" ISOLATED uid="); pw.print(uid); } - pw.println(); + pw.print(" gids={"); + if (gids != null) { + for (int gi=0; gi<gids.length; gi++) { + if (gi != 0) pw.print(", "); + pw.print(gids[gi]); + + } + } + pw.println("}"); + pw.print(prefix); pw.print("requiredAbi="); pw.print(requiredAbi); + pw.print(" instructionSet="); pw.println(instructionSet); if (info.className != null) { pw.print(prefix); pw.print("class="); pw.println(info.className); } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index d4a378b..1cb1bb8 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -105,6 +105,8 @@ final class ServiceRecord extends Binder { long restartDelay; // delay until next restart attempt. long restartTime; // time of last restart. long nextRestartTime; // time when restartDelay will expire. + boolean destroying; // set when we have started destroying the service + long destroyTime; // time at which destory was initiated. String stringName; // caching of toString @@ -250,6 +252,12 @@ final class ServiceRecord extends Binder { TimeUtils.formatDuration(executingStart, now, pw); pw.println(); } + if (destroying || destroyTime != 0) { + pw.print(prefix); pw.print("destroying="); pw.print(destroying); + pw.print(" destroyTime="); + TimeUtils.formatDuration(destroyTime, now, pw); + pw.println(); + } if (crashCount != 0 || restartCount != 0 || restartDelay != 0 || nextRestartTime != 0) { pw.print(prefix); pw.print("restartCount="); pw.print(restartCount); diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index 629a05d..24c723f 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -132,6 +132,8 @@ public class TaskPersister { // Data organization: <packageNameOfAffiliateTask, listOfAffiliatedTasksChains> private ArrayMap<String, List<List<OtherDeviceTask>>> mOtherDeviceTasksMap = new ArrayMap<>(10); + // Local cache of package names to uid used when restoring a task from another device. + private ArrayMap<String, Integer> mPackageUidMap; // The next time in milliseconds we will remove expired task from // {@link #mOtherDeviceTasksMap} and disk. Set to {@link Long.MAX_VALUE} to never clean-up @@ -579,7 +581,12 @@ public class TaskPersister { private void removeExpiredTasksIfNeeded() { synchronized (mOtherDeviceTasksMap) { final long now = System.currentTimeMillis(); - if (mOtherDeviceTasksMap.isEmpty() || now < mExpiredTasksCleanupTime) { + final boolean noMoreTasks = mOtherDeviceTasksMap.isEmpty(); + if (noMoreTasks || now < mExpiredTasksCleanupTime) { + if (noMoreTasks && mPackageUidMap != null) { + // All done! package->uid map no longer needed. + mPackageUidMap = null; + } return; } @@ -632,6 +639,20 @@ public class TaskPersister { if (DEBUG_RESTORER) Slog.d(TAG, "Reset expiration time to " + DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME)); + } else { + // All done! package->uid map no longer needed. + mPackageUidMap = null; + } + } + } + + /** + * Removes the input package name from the local package->uid map. + */ + void removeFromPackageCache(String packageName) { + synchronized (mOtherDeviceTasksMap) { + if (mPackageUidMap != null) { + mPackageUidMap.remove(packageName); } } } @@ -778,6 +799,27 @@ public class TaskPersister { task.mAffiliatedTaskId = INVALID_TASK_ID; task.mPrevAffiliateTaskId = INVALID_TASK_ID; task.mNextAffiliateTaskId = INVALID_TASK_ID; + // Set up uids valid for this device. + Integer uid = mPackageUidMap.get(task.realActivity.getPackageName()); + if (uid == null) { + // How did this happen??? + Slog.wtf(TAG, "Can't find uid for task=" + task + + " in mPackageUidMap=" + mPackageUidMap); + return null; + } + task.effectiveUid = task.mCallingUid = uid; + for (int i = task.mActivities.size() - 1; i >= 0; --i) { + final ActivityRecord activity = task.mActivities.get(i); + uid = mPackageUidMap.get(activity.launchedFromPackage); + if (uid == null) { + // How did this happen?? + Slog.wtf(TAG, "Can't find uid for activity=" + activity + + " in mPackageUidMap=" + mPackageUidMap); + return null; + } + activity.launchedFromUid = uid; + } + } else { Slog.e(TAG, "Unable to create task for backed-up file=" + file + ": " + fileToString(file)); @@ -801,42 +843,81 @@ public class TaskPersister { /** * Returns true if the input task chain backed-up from another device can be restored on this - * device. + * device. Also, sets the {@link OtherDeviceTask#mUid} on the input tasks if they can be + * restored. */ private boolean canAddOtherDeviceTaskChain(List<OtherDeviceTask> chain) { - // Get component names of all the tasks in the chain. - // Mainly doing this to reduce checking for a component twice if two or more - // affiliations belong to the same component which is highly likely. - ArraySet<ComponentName> componentsToCheck = new ArraySet<>(); + final ArraySet<ComponentName> validComponents = new ArraySet<>(); + final IPackageManager pm = AppGlobals.getPackageManager(); for (int i = 0; i < chain.size(); i++) { OtherDeviceTask task = chain.get(i); // Quick check, we can't add the task chain if any of its task files don't exist. if (!task.mFile.exists()) { - if (DEBUG_RESTORER) - Slog.d(TAG, "Can't add chain due to missing file=" + task.mFile); + if (DEBUG_RESTORER) Slog.d(TAG, + "Can't add chain due to missing file=" + task.mFile); + return false; + } + + // Verify task package is installed. + if (!isPackageInstalled(task.mComponentName.getPackageName())) { + return false; + } + // Verify that all the launch packages are installed. + if (task.mLaunchPackages != null) { + for (int j = task.mLaunchPackages.size() - 1; j >= 0; --j) { + if (!isPackageInstalled(task.mLaunchPackages.valueAt(j))) { + return false; + } + } + } + + if (validComponents.contains(task.mComponentName)) { + // Existance of component has already been verified. + continue; + } + + // Check to see if the specific component is installed. + try { + if (pm.getActivityInfo(task.mComponentName, 0, UserHandle.USER_OWNER) == null) { + // Component isn't installed... + return false; + } + validComponents.add(task.mComponentName); + } catch (RemoteException e) { + // Should not happen??? return false; } - componentsToCheck.add(task.mComponentName); } - boolean canAdd = true; + return true; + } + + /** + * Returns true if the input package name is installed. If the package is installed, an entry + * for the package is added to {@link #mPackageUidMap}. + */ + private boolean isPackageInstalled(final String packageName) { + if (mPackageUidMap != null && mPackageUidMap.containsKey(packageName)) { + return true; + } try { - // Check to see if all the components for this task chain are installed. - final IPackageManager pm = AppGlobals.getPackageManager(); - for (int i = 0; canAdd && i < componentsToCheck.size(); i++) { - ComponentName cn = componentsToCheck.valueAt(i); - canAdd &= pm.getActivityInfo(cn, 0, UserHandle.USER_OWNER) != null; - if (DEBUG_RESTORER) Slog.d(TAG, "ComponentName=" + cn + " installed=" + canAdd); + int uid = AppGlobals.getPackageManager().getPackageUid( + packageName, UserHandle.USER_OWNER); + if (uid == -1) { + // package doesn't exist... + return false; } + if (mPackageUidMap == null) { + mPackageUidMap = new ArrayMap<>(); + } + mPackageUidMap.put(packageName, uid); + return true; } catch (RemoteException e) { // Should not happen??? - canAdd = false; + return false; } - - if (DEBUG_RESTORER) Slog.d(TAG, "canAdd=" + canAdd); - return canAdd; } private class LazyTaskWriterThread extends Thread { @@ -993,12 +1074,17 @@ public class TaskPersister { final int mTaskId; final int mAffiliatedTaskId; - private OtherDeviceTask( - File file, ComponentName componentName, int taskId, int affiliatedTaskId) { + // Names of packages that launched activities in this task. All packages listed here need + // to be installed on the current device in order for the task to be restored successfully. + final ArraySet<String> mLaunchPackages; + + private OtherDeviceTask(File file, ComponentName componentName, int taskId, + int affiliatedTaskId, ArraySet<String> launchPackages) { mFile = file; mComponentName = componentName; mTaskId = taskId; mAffiliatedTaskId = (affiliatedTaskId == INVALID_TASK_ID) ? taskId: affiliatedTaskId; + mLaunchPackages = launchPackages; } @Override @@ -1036,6 +1122,7 @@ public class TaskPersister { final String name = in.getName(); if (TAG_TASK.equals(name)) { + final int outerDepth = in.getDepth(); ComponentName componentName = null; int taskId = INVALID_TASK_ID; int taskAffiliation = INVALID_TASK_ID; @@ -1056,10 +1143,31 @@ public class TaskPersister { + " taskId=" + taskId + " file=" + file); return null; } + + ArraySet<String> launchPackages = null; + while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && + (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { + if (event == XmlPullParser.START_TAG) { + if (TaskRecord.TAG_ACTIVITY.equals(in.getName())) { + for (int j = in.getAttributeCount() - 1; j >= 0; --j) { + if (ActivityRecord.ATTR_LAUNCHEDFROMPACKAGE.equals( + in.getAttributeName(j))) { + if (launchPackages == null) { + launchPackages = new ArraySet(); + } + launchPackages.add(in.getAttributeValue(j)); + } + } + } else { + XmlUtils.skipCurrentTag(in); + } + } + } if (DEBUG_RESTORER) Slog.d(TAG, "creating OtherDeviceTask from file=" + file.getName() + " componentName=" + componentName - + " taskId=" + taskId); - return new OtherDeviceTask(file, componentName, taskId, taskAffiliation); + + " taskId=" + taskId + " launchPackages=" + launchPackages); + return new OtherDeviceTask(file, componentName, taskId, + taskAffiliation, launchPackages); } else { Slog.wtf(TAG, "createFromFile: Unknown xml event=" + event + " name=" + name); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index c3eda71..60f8a48 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -59,7 +59,7 @@ final class TaskRecord { private static final String TAG_AFFINITYINTENT = "affinity_intent"; static final String ATTR_REALACTIVITY = "real_activity"; private static final String ATTR_ORIGACTIVITY = "orig_activity"; - private static final String TAG_ACTIVITY = "activity"; + static final String TAG_ACTIVITY = "activity"; private static final String ATTR_AFFINITY = "affinity"; private static final String ATTR_ROOT_AFFINITY = "root_affinity"; private static final String ATTR_ROOTHASRESET = "root_has_reset"; diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index 9fa362c..87f78c1 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -28,6 +28,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; +import android.net.ProxyInfo; import android.net.TrafficStats; import android.net.Uri; import android.net.wifi.WifiInfo; @@ -68,7 +69,7 @@ import java.util.Random; public class NetworkMonitor extends StateMachine { private static final boolean DBG = true; private static final String TAG = "NetworkMonitor"; - private static final String DEFAULT_SERVER = "clients3.google.com"; + private static final String DEFAULT_SERVER = "connectivitycheck.android.com"; private static final int SOCKET_TIMEOUT_MS = 10000; public static final String ACTION_NETWORK_CONDITIONS_MEASURED = "android.net.conn.NETWORK_CONDITIONS_MEASURED"; @@ -163,6 +164,7 @@ public class NetworkMonitor extends StateMachine { /** * Force evaluation even if it has succeeded in the past. * arg1 = UID responsible for requesting this reeval. Will be billed for data. + * arg2 = Number of evaluation attempts to make. (If 0, make INITIAL_ATTEMPTS attempts.) */ public static final int CMD_FORCE_REEVALUATION = BASE + 8; @@ -212,11 +214,14 @@ public class NetworkMonitor extends StateMachine { // Negative values disable reevaluation. private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay"; - // Default to 5s reevaluation delay. + // When connecting, attempt to validate 3 times, pausing 5s between them. private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000; - private static final int MAX_RETRIES = 10; - // Between groups of MAX_RETRIES evaluation attempts, pause 10 mins in hopes ISP outage passes. + private static final int INITIAL_ATTEMPTS = 3; + // If a network is not validated, make one attempt every 10 mins to see if it starts working. private static final int REEVALUATE_PAUSE_MS = 10*60*1000; + private static final int PERIODIC_ATTEMPTS = 1; + // When an application calls reportBadNetwork, only make one attempt. + private static final int REEVALUATE_ATTEMPTS = 1; private final int mReevaluateDelayMs; private int mReevaluateToken = 0; private static final int INVALID_UID = -1; @@ -236,6 +241,14 @@ public class NetworkMonitor extends StateMachine { // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. private boolean mUserDoesNotWant = false; + // How many times we should attempt validation. Only checked in EvaluatingState; must be set + // before entering EvaluatingState. Note that whatever code causes us to transition to + // EvaluatingState last decides how many attempts will be made, so if one codepath were to + // enter EvaluatingState with a specific number of attempts, and then another were to enter it + // with a different number of attempts, the second number would be used. This is not currently + // a problem because EvaluatingState is not reentrant. + private int mMaxAttempts; + public boolean systemReady = false; private final State mDefaultState = new DefaultState(); @@ -305,6 +318,7 @@ public class NetworkMonitor extends StateMachine { return HANDLED; case CMD_NETWORK_CONNECTED: if (DBG) log("Connected"); + mMaxAttempts = INITIAL_ATTEMPTS; transitionTo(mEvaluatingState); return HANDLED; case CMD_NETWORK_DISCONNECTED: @@ -318,6 +332,7 @@ public class NetworkMonitor extends StateMachine { case CMD_FORCE_REEVALUATION: if (DBG) log("Forcing reevaluation"); mUidResponsibleForReeval = message.arg1; + mMaxAttempts = message.arg2 != 0 ? message.arg2 : REEVALUATE_ATTEMPTS; transitionTo(mEvaluatingState); return HANDLED; case CMD_CAPTIVE_PORTAL_APP_FINISHED: @@ -347,7 +362,10 @@ public class NetworkMonitor extends StateMachine { public void enter() { mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo)); - if (!mUserDoesNotWant) sendMessageDelayed(CMD_FORCE_REEVALUATION, REEVALUATE_PAUSE_MS); + if (!mUserDoesNotWant) { + sendMessageDelayed(CMD_FORCE_REEVALUATION, 0 /* no UID */, + PERIODIC_ATTEMPTS, REEVALUATE_PAUSE_MS); + } } @Override @@ -413,11 +431,11 @@ public class NetworkMonitor extends StateMachine { // Being in the EvaluatingState State indicates the Network is being evaluated for internet // connectivity. private class EvaluatingState extends State { - private int mRetries; + private int mAttempt; @Override public void enter() { - mRetries = 0; + mAttempt = 1; sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); if (mUidResponsibleForReeval != INVALID_UID) { TrafficStats.setThreadStatsUid(mUidResponsibleForReeval); @@ -454,18 +472,18 @@ public class NetworkMonitor extends StateMachine { transitionTo(mValidatedState); return HANDLED; } - // Note: This call to isCaptivePortal() could take minutes. Resolving the - // server's IP addresses could hit the DNS timeout and attempting connections - // to each of the server's several (e.g. 11) IP addresses could each take - // SOCKET_TIMEOUT_MS. During this time this StateMachine will be unresponsive. - // isCaptivePortal() could be executed on another Thread if this is found to - // cause problems. + // Note: This call to isCaptivePortal() could take up to a minute. Resolving the + // server's IP addresses could hit the DNS timeout, and attempting connections + // to each of the server's several IP addresses (currently one IPv4 and one + // IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine + // will be unresponsive. isCaptivePortal() could be executed on another Thread + // if this is found to cause problems. int httpResponseCode = isCaptivePortal(); if (httpResponseCode == 204) { transitionTo(mValidatedState); } else if (httpResponseCode >= 200 && httpResponseCode <= 399) { transitionTo(mCaptivePortalState); - } else if (++mRetries > MAX_RETRIES) { + } else if (++mAttempt > mMaxAttempts) { transitionTo(mOfflineState); } else if (mReevaluateDelayMs >= 0) { Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); @@ -639,12 +657,36 @@ public class NetworkMonitor extends StateMachine { int httpResponseCode = 599; try { URL url = new URL("http", mServer, "/generate_204"); + // On networks with a PAC instead of fetching a URL that should result in a 204 + // reponse, we instead simply fetch the PAC script. This is done for a few reasons: + // 1. At present our PAC code does not yet handle multiple PACs on multiple networks + // until something like https://android-review.googlesource.com/#/c/115180/ lands. + // Network.openConnection() will ignore network-specific PACs and instead fetch + // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with + // NO_PROXY is the fetch of the PAC itself. + // 2. To proxy the generate_204 fetch through a PAC would require a number of things + // happen before the fetch can commence, namely: + // a) the PAC script be fetched + // b) a PAC script resolver service be fired up and resolve mServer + // Network validation could be delayed until these prerequisities are satisifed or + // could simply be left to race them. Neither is an optimal solution. + // 3. PAC scripts are sometimes used to block or restrict Internet access and may in + // fact block fetching of the generate_204 URL which would lead to false negative + // results for network validation. + boolean fetchPac = false; + { + final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy(); + if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) { + url = new URL(proxyInfo.getPacFileUrl().toString()); + fetchPac = true; + } + } if (DBG) { log("Checking " + url.toString() + " on " + mNetworkAgentInfo.networkInfo.getExtraInfo()); } urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url); - urlConnection.setInstanceFollowRedirects(false); + urlConnection.setInstanceFollowRedirects(fetchPac); urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); urlConnection.setUseCaches(false); @@ -678,6 +720,11 @@ public class NetworkMonitor extends StateMachine { httpResponseCode = 204; } + if (httpResponseCode == 200 && fetchPac) { + if (DBG) log("PAC fetch 200 response interpreted as 204 response."); + httpResponseCode = 204; + } + sendNetworkConditionsBroadcast(true /* response received */, httpResponseCode != 204 /* isCaptivePortal */, requestTimestamp, responseTimestamp); diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index ef86c6c..5ff7022 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -20,6 +20,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -45,8 +46,10 @@ import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.Log; +import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.IState; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -63,6 +66,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + /** * @hide @@ -976,6 +981,12 @@ public class Tethering extends BaseNetworkObserver { if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); } try { + mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName); + } catch (Exception e) { + if (VDBG) Log.e( + TAG, "Exception in removeInterfaceForward: " + e.toString()); + } + try { mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName); } catch (Exception e) { if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); @@ -1028,9 +1039,14 @@ public class Tethering extends BaseNetworkObserver { if (newUpstreamIfaceName != null) { try { mNMService.enableNat(mIfaceName, newUpstreamIfaceName); + mNMService.startInterfaceForwarding(mIfaceName, + newUpstreamIfaceName); } catch (Exception e) { Log.e(TAG, "Exception enabling Nat: " + e.toString()); try { + mNMService.disableNat(mIfaceName, newUpstreamIfaceName); + } catch (Exception ee) {} + try { mNMService.untetherInterface(mIfaceName); } catch (Exception ee) {} @@ -1377,6 +1393,112 @@ public class Tethering extends BaseNetworkObserver { } } + private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0); + private SimChangeBroadcastReceiver mBroadcastReceiver = null; + + // keep consts in sync with packages/apps/Settings TetherSettings.java + private static final int WIFI_TETHERING = 0; + private static final int USB_TETHERING = 1; + private static final int BLUETOOTH_TETHERING = 2; + + // keep consts in sync with packages/apps/Settings TetherService.java + private static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; + private static final String EXTRA_RUN_PROVISION = "extraRunProvision"; + + private void startListeningForSimChanges() { + if (DBG) Log.d(TAG, "startListeningForSimChanges"); + if (mBroadcastReceiver == null) { + mBroadcastReceiver = new SimChangeBroadcastReceiver( + mSimBcastGenerationNumber.incrementAndGet()); + final IntentFilter filter = new IntentFilter(); + filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + + mContext.registerReceiver(mBroadcastReceiver, filter); + } + } + + private void stopListeningForSimChanges() { + if (DBG) Log.d(TAG, "stopListeningForSimChanges"); + if (mBroadcastReceiver != null) { + mSimBcastGenerationNumber.incrementAndGet(); + mContext.unregisterReceiver(mBroadcastReceiver); + mBroadcastReceiver = null; + } + } + + class SimChangeBroadcastReceiver extends BroadcastReceiver { + // used to verify this receiver is still current + final private int mGenerationNumber; + + // we're interested in edge-triggered LOADED notifications, so + // ignore LOADED unless we saw an ABSENT state first + private boolean mSimAbsentSeen = false; + + public SimChangeBroadcastReceiver(int generationNumber) { + super(); + mGenerationNumber = generationNumber; + } + + @Override + public void onReceive(Context context, Intent intent) { + if (DBG) { + Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber + + ", current generationNumber=" + mSimBcastGenerationNumber.get()); + } + if (mGenerationNumber != mSimBcastGenerationNumber.get()) return; + + final String state = + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + + Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" + + mSimAbsentSeen); + if (!mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) { + mSimAbsentSeen = true; + } + + if (mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) { + mSimAbsentSeen = false; + try { + if (mContext.getResources().getString(com.android.internal.R.string. + config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) { + final String tetherService = mContext.getResources().getString( + com.android.internal.R.string.config_wifi_tether_enable); + ArrayList<Integer> tethered = new ArrayList<Integer>(); + synchronized (mPublicSync) { + Set ifaces = mIfaces.keySet(); + for (Object iface : ifaces) { + TetherInterfaceSM sm = mIfaces.get(iface); + if (sm != null && sm.isTethered()) { + if (isUsb((String)iface)) { + tethered.add(new Integer(USB_TETHERING)); + } else if (isWifi((String)iface)) { + tethered.add(new Integer(WIFI_TETHERING)); + } else if (isBluetooth((String)iface)) { + tethered.add(new Integer(BLUETOOTH_TETHERING)); + } + } + } + } + for (int tetherType : tethered) { + Intent startProvIntent = new Intent(); + startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType); + startProvIntent.putExtra(EXTRA_RUN_PROVISION, true); + startProvIntent.setComponent( + ComponentName.unflattenFromString(tetherService)); + mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT); + } + Log.d(TAG, "re-evaluate provisioning"); + } else { + Log.d(TAG, "no prov-check needed for new SIM"); + } + } catch (Resources.NotFoundException e) { + Log.d(TAG, "no prov-check needed for new SIM"); + // not defined, do nothing + } + } + } + } + class InitialState extends TetherMasterUtilState { @Override public void enter() { @@ -1413,6 +1535,7 @@ public class Tethering extends BaseNetworkObserver { @Override public void enter() { turnOnMasterTetherSettings(); // may transition us out + startListeningForSimChanges(); mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; // better try something first pass // or crazy tests cases will fail @@ -1422,6 +1545,7 @@ public class Tethering extends BaseNetworkObserver { @Override public void exit() { turnOffUpstreamMobileConnection(); + stopListeningForSimChanges(); notifyTetheredOfNewUpstreamIface(null); } @Override diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index f08a652..8533f69 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -846,9 +846,29 @@ public class Vpn { /** * Start legacy VPN, controlling native daemons as needed. Creates a * secondary thread to perform connection work, returning quickly. + * + * Should only be called to respond to Binder requests as this enforces caller permission. Use + * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the + * permission check only when the caller is trusted (or the call is initiated by the system). */ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) { enforceControlPermission(); + long token = Binder.clearCallingIdentity(); + try { + startLegacyVpnPrivileged(profile, keyStore, egress); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check + * permissions under the assumption that the caller is the system. + * + * Callers are responsible for checking permissions if needed. + */ + public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore, + LinkProperties egress) { if (!keyStore.isUnlocked()) { throw new IllegalStateException("KeyStore isn't unlocked"); } @@ -959,10 +979,10 @@ public class Vpn { } private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { - stopLegacyVpn(); + stopLegacyVpnPrivileged(); - // Prepare for the new request. This also checks the caller. - prepare(null, VpnConfig.LEGACY_VPN); + // Prepare for the new request. + prepareInternal(VpnConfig.LEGACY_VPN); updateState(DetailedState.CONNECTING, "startLegacyVpn"); // Start a new LegacyVpnRunner and we are done! @@ -970,7 +990,8 @@ public class Vpn { mLegacyVpnRunner.start(); } - public synchronized void stopLegacyVpn() { + /** Stop legacy VPN. Permissions must be checked by callers. */ + public synchronized void stopLegacyVpnPrivileged() { if (mLegacyVpnRunner != null) { mLegacyVpnRunner.exit(); mLegacyVpnRunner = null; diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index f549f3d..6e61e41 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -464,13 +464,13 @@ final class ColorFade { try { SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), s); + st.updateTexImage(); + st.getTransformMatrix(mTexMatrix); } finally { s.release(); + st.release(); } - st.updateTexImage(); - st.getTransformMatrix(mTexMatrix); - // Set up texture coordinates for a quad. // We might need to change this if the texture ends up being // a different size from the display for some reason. diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java index 59d5605..7f48768 100644 --- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java +++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java @@ -62,11 +62,6 @@ final class ActiveSourceHandler { void process(ActiveSource newActive, int deviceType) { // Seq #17 HdmiCecLocalDeviceTv tv = mSource; - ActiveSource activeSource = tv.getActiveSource(); - if (activeSource.equals(newActive)) { - invokeCallback(HdmiControlManager.RESULT_SUCCESS); - return; - } HdmiDeviceInfo device = mService.getDeviceInfo(newActive.logicalAddress); if (device == null) { tv.startNewDeviceAction(newActive, deviceType); diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index f6d4efd..0c86aed 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -230,13 +230,17 @@ final class Constants { static final int OPTION_CEC_ENABLE = 2; // If set to disabled, system service yields control of CEC to sub-microcontroller. - // If enabled, it take the control back. + // If enabled, it takes the control back. static final int OPTION_CEC_SERVICE_CONTROL = 3; // Put other devices to standby when TV goes to standby. enabled by default. // If set to disabled, TV doesn't send <Standby> to other devices. static final int OPTION_CEC_AUTO_DEVICE_OFF = 4; + // Passes the language used in the system when updated. The value to use is the 3 byte + // code as defined in ISO/FDIS 639-2. + static final int OPTION_CEC_SET_LANGUAGE = 5; + // If set to disabled, TV does not switch ports when mobile device is connected. static final int OPTION_MHL_INPUT_SWITCHING = 101; @@ -246,6 +250,10 @@ final class Constants { // If set to disabled, all MHL commands are discarded. static final int OPTION_MHL_ENABLE = 103; + // If set to disabled, system service yields control of MHL to sub-microcontroller. + // If enabled, it takes the control back. + static final int OPTION_MHL_SERVICE_CONTROL = 104; + static final int DISABLED = 0; static final int ENABLED = 1; diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java index 68311de..77ffe0b 100644 --- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java +++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java @@ -54,6 +54,7 @@ final class DelayedMessageBuffer { mBuffer.add(message); break; case Constants.MESSAGE_INITIATE_ARC: + case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE: mBuffer.add(message); break; default: @@ -75,36 +76,51 @@ final class DelayedMessageBuffer { } } - void processAllMessages() { + boolean isBuffered(int opcode) { for (HdmiCecMessage message : mBuffer) { + if (message.getOpcode() == opcode) { + return true; + } + } + return false; + } + + void processAllMessages() { + // Use the copied buffer. + ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer); + mBuffer.clear(); + for (HdmiCecMessage message : copiedBuffer) { 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()}. + * <p><Active Source> is processed only when the TV input is ready. + * If not, {@link #processActiveSource()} will be invoked later to handle it. * * @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) { + ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer); + mBuffer.clear(); + HdmiLogger.debug("Checking message for address:" + address); + for (HdmiCecMessage message : copiedBuffer) { + if (message.getSource() != address) { + mBuffer.add(message); continue; } - if (message.getSource() == address) { - mDevice.onMessage(message); - HdmiLogger.debug("Processing message:" + message); - iter.remove(); + if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE + && !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) { + mBuffer.add(message); + continue; } + mDevice.onMessage(message); + HdmiLogger.debug("Processing message:" + message); } } @@ -120,13 +136,15 @@ final class DelayedMessageBuffer { * @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(); + ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer); + mBuffer.clear(); + for (HdmiCecMessage message : copiedBuffer) { if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE && message.getSource() == address) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); - iter.remove(); + } else { + mBuffer.add(message); } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 4f8b9fb..d17e9b3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -191,6 +191,25 @@ abstract class HdmiCecLocalDevice { protected abstract void setPreferredAddress(int addr); /** + * Returns true if the TV input associated with the CEC device is ready + * to accept further processing such as input switching. This is used + * to buffer certain CEC commands and process it later if the input is not + * ready yet. For other types of local devices(non-TV), this method returns + * true by default to let the commands be processed right away. + */ + protected boolean isInputReady(int deviceId) { + return true; + } + + /** + * Returns true if the local device allows the system to be put to standby. + * The default implementation returns true. + */ + protected boolean canGoToStandby() { + return true; + } + + /** * Dispatch incoming message. * * @param message incoming message diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 85a1a15..a8f6954 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -19,6 +19,8 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; import android.os.RemoteException; import android.os.SystemProperties; import android.util.Slog; @@ -34,17 +36,17 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { private boolean mIsActiveSource = false; + // Used to keep the device awake while it is the active source. For devices that + // cannot wake up via CEC commands, this address the inconvenience of having to + // turn them on. + // Lazily initialized - should call getWakeLock() to get the instance. + private WakeLock mWakeLock; + HdmiCecLocalDevicePlayback(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_PLAYBACK); } @Override - void init() { - super.init(); - mIsActiveSource = false; - } - - @Override @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); @@ -129,12 +131,37 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { if (connected && mService.isPowerStandbyOrTransient()) { mService.wakeUp(); } + if (!connected) { + getWakeLock().release(); + } } @ServiceThreadOnly - void markActiveSource() { + void setActiveSource(boolean on) { assertRunOnServiceThread(); - mIsActiveSource = true; + mIsActiveSource = on; + if (on) { + getWakeLock().acquire(); + HdmiLogger.debug("active source: %b. Wake lock acquired", mIsActiveSource); + } else { + getWakeLock().release(); + HdmiLogger.debug("Wake lock released"); + } + } + + @ServiceThreadOnly + private WakeLock getWakeLock() { + assertRunOnServiceThread(); + if (mWakeLock == null) { + mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + mWakeLock.setReferenceCounted(false); + } + return mWakeLock; + } + + @Override + protected boolean canGoToStandby() { + return !getWakeLock().isHeld(); } @Override @@ -148,7 +175,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { private void mayResetActiveSource(int physicalAddress) { if (physicalAddress != mService.getPhysicalAddress()) { - mIsActiveSource = false; + setActiveSource(false); } } @@ -163,9 +190,9 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { return true; // Broadcast message. } - // Samsung model, we tested, sends <RoutingChange> and <RequestActiveSource> consecutively, - // Then if there is no <ActiveSource> response, it will change the input to - // the internal source. To handle this, we'll set ActiveSource aggressively. + // Samsung model we tested sends <Routing Change> and <Request Active Source> + // in a row, and then changes the input to the internal source if there is no + // <Active Source> in response. To handle this, we'll set ActiveSource aggressively. @Override @ServiceThreadOnly protected boolean handleRoutingChange(HdmiCecMessage message) { @@ -185,11 +212,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { } private void maySetActiveSource(int physicalAddress) { - if (physicalAddress == mService.getPhysicalAddress()) { - mIsActiveSource = true; - } else { - mIsActiveSource = false; - } + setActiveSource(physicalAddress == mService.getPhysicalAddress()); } private void wakeUpIfActiveSource() { @@ -226,7 +249,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( mAddress, mService.getPhysicalAddress())); } - mIsActiveSource = false; + setActiveSource(false); checkIfPendingActionsCleared(); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index f40010d..8241cdc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -34,6 +34,7 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.HdmiRecordSources; import android.hardware.hdmi.HdmiTimerRecordSources; import android.hardware.hdmi.IHdmiControlCallback; @@ -63,6 +64,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.HashMap; /** * Represent a logical device of type TV residing in Android system. @@ -143,12 +145,44 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { public void onInputAdded(String inputId) { TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId); HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo(); - if (info != null && info.isCecDevice()) { - mDelayedMessageBuffer.processActiveSource(info.getLogicalAddress()); + if (info == null) return; + addTvInput(inputId, info.getId()); + if (info.isCecDevice()) { + processDelayedActiveSource(info.getLogicalAddress()); } } + + @Override + public void onInputRemoved(String inputId) { + removeTvInput(inputId); + } }; + // Keeps the mapping (TV input ID, HDMI device ID) to keep track of the TV inputs ready to + // accept input switching request from HDMI devices. Requests for which the corresponding + // input ID is not yet registered by TV input framework need to be buffered for delayed + // processing. + private final HashMap<String, Integer> mTvInputs = new HashMap<>(); + + @ServiceThreadOnly + private void addTvInput(String inputId, int deviceId) { + assertRunOnServiceThread(); + mTvInputs.put(inputId, deviceId); + } + + @ServiceThreadOnly + private void removeTvInput(String inputId) { + assertRunOnServiceThread(); + mTvInputs.remove(inputId); + } + + @Override + @ServiceThreadOnly + protected boolean isInputReady(int deviceId) { + assertRunOnServiceThread(); + return mTvInputs.containsValue(deviceId); + } + HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_TV); mPrevPortId = Constants.INVALID_PORT_ID; @@ -168,6 +202,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( mAddress, mService.getVendorId())); mCecSwitches.add(mService.getPhysicalAddress()); // TV is a CEC switch too. + mTvInputs.clear(); mSkipRoutingControl = (reason == HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE); launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC && reason != HdmiControlService.INITIATED_BY_BOOT_UP); @@ -188,18 +223,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } @Override - @ServiceThreadOnly protected int getPreferredAddress() { - assertRunOnServiceThread(); - return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_TV, - Constants.ADDR_UNREGISTERED); + return Constants.ADDR_TV; } @Override - @ServiceThreadOnly protected void setPreferredAddress(int addr) { - assertRunOnServiceThread(); - SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_TV, String.valueOf(addr)); + Slog.w(TAG, "Preferred addres will not be stored for TV"); } @Override @@ -326,8 +356,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { ActiveSource activeSource = getActiveSource(); HdmiDeviceInfo info = getCecDeviceInfo(activeSource.logicalAddress); if (info == null) { - info = new HdmiDeviceInfo(Constants.ADDR_INVALID, path, getActivePortId(), - HdmiDeviceInfo.DEVICE_RESERVED, 0, null); + info = mService.getDeviceInfoByPort(getActivePortId()); + if (info == null) { + // No CEC/MHL device is present at the port. Attempt to switch to + // the hardware port itself for non-CEC devices that may be connected. + info = new HdmiDeviceInfo(path, getActivePortId()); + } } mService.invokeInputChangeListener(info); } @@ -398,11 +432,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return; } List<SendKeyAction> action = getActions(SendKeyAction.class); + int logicalAddress = findKeyReceiverAddress(); + if (logicalAddress == mAddress) { + Slog.w(TAG, "Discard key event to itself :" + keyCode + " pressed:" + isPressed); + return; + } if (!action.isEmpty()) { action.get(0).processKeyEvent(keyCode, isPressed); } else { if (isPressed) { - int logicalAddress = findKeyReceiverAddress(); if (logicalAddress != Constants.ADDR_INVALID) { addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode)); return; @@ -443,8 +481,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); if (info == null) { if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) { + HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress); mDelayedMessageBuffer.add(message); } + } else if (!isInputReady(info.getId())) { + HdmiLogger.debug("Input not ready for device: %X; buffering the command", info.getId()); + mDelayedMessageBuffer.add(message); } else { ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); @@ -480,6 +522,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { doManualPortSwitching(portId, null); setPrevPortId(Constants.INVALID_PORT_ID); + } else { + // No HDMI port to switch to was found. Notify the input change listers to + // switch to the lastly shown internal input. + mActiveSource.invalidate(); + setActivePath(Constants.INVALID_PHYSICAL_ADDRESS); + mService.invokeInputChangeListener(HdmiDeviceInfo.INACTIVE_DEVICE); } return true; } @@ -537,6 +585,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (!isInDeviceList(address, path)) { handleNewDeviceAtTheTailOfActivePath(path); } + + // Add the device ahead with default information to handle <Active Source> + // promptly, rather than waiting till the new device action is finished. + HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(address, path, getPortId(path), type, + Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address)); + addCecDevice(deviceInfo); startNewDeviceAction(ActiveSource.of(address, path), type); return true; } @@ -742,7 +796,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void onNewAvrAdded(HdmiDeviceInfo avr) { assertRunOnServiceThread(); - if (getSystemAudioModeSetting()) { + if (getSystemAudioModeSetting() && !isSystemAudioActivated()) { addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); } if (isArcFeatureEnabled() && !hasAction(SetArcTransmissionStateAction.class)) { @@ -835,6 +889,17 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return oldStatus; } + @ServiceThreadOnly + private void updateArcFeatureStatus(int portId, boolean isConnected) { + assertRunOnServiceThread(); + // HEAC 2.4, HEACT 5-15 + // Should not activate ARC if +5V status is false. + HdmiPortInfo portInfo = mService.getPortInfo(portId); + if (portInfo.isArcSupported()) { + changeArcFeatureEnabled(isConnected); + } + } + private void notifyArcStatusToAudioService(boolean enabled) { // Note that we don't set any name to ARC. mService.getAudioManager().setWiredDeviceConnectionState( @@ -1037,6 +1102,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { protected boolean handleSetSystemAudioMode(HdmiCecMessage message) { assertRunOnServiceThread(); if (!isMessageForSystemAudio(message)) { + if (getAvrDeviceInfo() == null) { + // AVR may not have been discovered yet. Delay the message processing. + mDelayedMessageBuffer.add(message); + return true; + } HdmiLogger.warning("Invalid <Set System Audio Mode> message:" + message); mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); return true; @@ -1380,7 +1450,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } else { int activePath = mService.getPhysicalAddress(); setActivePath(activePath); - if (!routingForBootup) { + if (!routingForBootup + && !mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) { mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(mAddress, activePath)); } @@ -1407,6 +1478,25 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } /** + * Returns the {@link HdmiDeviceInfo} instance whose physical address matches + * the given routing path. This is the version accessible safely from threads + * other than service thread. + * + * @param path routing path or physical address + * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null + */ + HdmiDeviceInfo getSafeDeviceInfoByPath(int path) { + synchronized (mLock) { + for (HdmiDeviceInfo info : mSafeAllDeviceInfos) { + if (info.getPhysicalAddress() == path) { + return info; + } + } + return null; + } + } + + /** * Whether a device of the specified physical address and logical address exists * in a device info list. However, both are minimal condition and it could * be different device from the original one. @@ -1416,7 +1506,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { * @return true if exist; otherwise false */ @ServiceThreadOnly - private boolean isInDeviceList(int logicalAddress, int physicalAddress) { + boolean isInDeviceList(int logicalAddress, int physicalAddress) { assertRunOnServiceThread(); HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress); if (device == null) { @@ -1441,6 +1531,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // It covers seq #40, #43. hotplugActions.get(0).pollAllDevicesNow(); } + updateArcFeatureStatus(portId, connected); } private void removeCecSwitches(int portId) { @@ -1583,7 +1674,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource)); announceOneTouchRecordResult(recorderAddress, ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN); - return Constants.ABORT_UNABLE_TO_DETERMINE; + return Constants.ABORT_CANNOT_PROVIDE_SOURCE; } addAndStartAction(new OneTouchRecordAction(this, recorderAddress, recordSource)); @@ -1767,6 +1858,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mDelayedMessageBuffer.processMessagesForDevice(address); } + @ServiceThreadOnly + void processDelayedActiveSource(int address) { + assertRunOnServiceThread(); + mDelayedMessageBuffer.processActiveSource(address); + } + @Override protected void dump(final IndentingPrintWriter pw) { super.dump(pw); @@ -1777,6 +1874,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { pw.println("mAutoDeviceOff: " + mAutoDeviceOff); pw.println("mAutoWakeup: " + mAutoWakeup); pw.println("mSkipRoutingControl: " + mSkipRoutingControl); + pw.println("mPrevPortId: " + mPrevPortId); pw.println("CEC devices:"); pw.increaseIndent(); for (HdmiDeviceInfo info : mSafeAllDeviceInfos) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 8ce6caf..49a96d8 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -23,9 +23,11 @@ import static com.android.server.hdmi.Constants.ENABLED; import static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP; import static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE; import static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL; +import static com.android.server.hdmi.Constants.OPTION_CEC_SET_LANGUAGE; import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE; import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING; import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE; +import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL; import android.annotation.Nullable; import android.content.BroadcastReceiver; @@ -277,12 +279,78 @@ public final class HdmiControlService extends SystemService { @Nullable private TvInputManager mTvInputManager; + @Nullable + private PowerManager mPowerManager; + // 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. @ServiceThreadOnly private int mLastInputMhl = Constants.INVALID_PORT_ID; + // Set to true if the logical address allocation is completed. + private boolean mAddressAllocated = false; + + // Buffer for processing the incoming cec messages while allocating logical addresses. + private final class CecMessageBuffer { + private List<HdmiCecMessage> mBuffer = new ArrayList<>(); + + public void bufferMessage(HdmiCecMessage message) { + switch (message.getOpcode()) { + case Constants.MESSAGE_ACTIVE_SOURCE: + bufferActiveSource(message); + break; + case Constants.MESSAGE_IMAGE_VIEW_ON: + case Constants.MESSAGE_TEXT_VIEW_ON: + bufferImageOrTextViewOn(message); + break; + // Add here if new message that needs to buffer + default: + // Do not need to buffer messages other than above + break; + } + } + + public void processMessages() { + for (final HdmiCecMessage message : mBuffer) { + runOnServiceThread(new Runnable() { + @Override + public void run() { + handleCecCommand(message); + } + }); + } + mBuffer.clear(); + } + + private void bufferActiveSource(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) { + mBuffer.add(message); + } + } + + private void bufferImageOrTextViewOn(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) && + !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) { + mBuffer.add(message); + } + } + + // Returns true if the message is replaced + private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) { + for (int i = 0; i < mBuffer.size(); i++) { + HdmiCecMessage bufferedMessage = mBuffer.get(i); + if (bufferedMessage.getOpcode() == opcode) { + mBuffer.set(i, message); + return true; + } + } + return false; + } + } + + private CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer(); + public HdmiControlService(Context context) { super(context); mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE)); @@ -346,6 +414,7 @@ public final class HdmiControlService extends SystemService { // Register ContentObserver to monitor the settings change. registerContentObserver(); } + mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED); } @Override @@ -353,6 +422,7 @@ public final class HdmiControlService extends SystemService { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mTvInputManager = (TvInputManager) getContext().getSystemService( Context.TV_INPUT_SERVICE); + mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); } } @@ -370,6 +440,10 @@ public final class HdmiControlService extends SystemService { mTvInputManager.unregisterCallback(callback); } + PowerManager getPowerManager() { + return mPowerManager; + } + /** * Called when the initialization of local devices is complete. */ @@ -466,7 +540,9 @@ public final class HdmiControlService extends SystemService { } private void initializeCec(int initiatedBy) { + mAddressAllocated = false; mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED); + mCecController.setOption(OPTION_CEC_SET_LANGUAGE, HdmiUtils.languageToInt(mLanguage)); initializeLocalDevices(initiatedBy); } @@ -496,6 +572,8 @@ public final class HdmiControlService extends SystemService { mCecController.clearLogicalAddress(); final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>(); final int[] finished = new int[1]; + mAddressAllocated = allocatingDevices.isEmpty(); + for (final HdmiCecLocalDevice localDevice : allocatingDevices) { mCecController.allocateLogicalAddress(localDevice.getType(), localDevice.getPreferredAddress(), new AllocateAddressCallback() { @@ -516,12 +594,14 @@ public final class HdmiControlService extends SystemService { // Address allocation completed for all devices. Notify each device. if (allocatingDevices.size() == ++finished[0]) { + mAddressAllocated = true; if (initiatedBy != INITIATED_BY_HOTPLUG) { // In case of the hotplug we don't call onInitializeCecComplete() // since we reallocate the logical address only. onInitializeCecComplete(initiatedBy); } notifyAddressAllocated(allocatedDevices, initiatedBy); + mCecMessageBuffer.processMessages(); } } }); @@ -672,6 +752,16 @@ public final class HdmiControlService extends SystemService { return tv() == null ? null : tv().getCecDeviceInfo(logicalAddress); } + @ServiceThreadOnly + HdmiDeviceInfo getDeviceInfoByPort(int port) { + assertRunOnServiceThread(); + HdmiMhlLocalDeviceStub info = mMhlController.getLocalDevice(port); + if (info != null) { + return info.getInfo(); + } + return null; + } + /** * Returns version of CEC. */ @@ -744,6 +834,10 @@ public final class HdmiControlService extends SystemService { @ServiceThreadOnly boolean handleCecCommand(HdmiCecMessage message) { assertRunOnServiceThread(); + if (!mAddressAllocated) { + mCecMessageBuffer.bufferMessage(message); + return true; + } int errorCode = mMessageValidator.isValid(message); if (errorCode != HdmiCecMessageValidator.OK) { // We'll not response on the messages with the invalid source or destination @@ -1102,7 +1196,8 @@ public final class HdmiControlService extends SystemService { } int activePath = tv.getActivePath(); if (activePath != HdmiDeviceInfo.PATH_INVALID) { - return new HdmiDeviceInfo(activePath, tv.getActivePortId()); + HdmiDeviceInfo info = tv.getSafeDeviceInfoByPath(activePath); + return (info != null) ? info : new HdmiDeviceInfo(activePath, tv.getActivePortId()); } return null; } @@ -1409,6 +1504,11 @@ public final class HdmiControlService extends SystemService { runOnServiceThread(new Runnable() { @Override public void run() { + HdmiMhlLocalDeviceStub mhlDevice = mMhlController.getLocalDeviceById(deviceId); + if (mhlDevice != null) { + mhlDevice.sendStandby(); + return; + } HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType); if (device == null) { Slog.w(TAG, "Local device not available"); @@ -1528,6 +1628,12 @@ public final class HdmiControlService extends SystemService { mCecController.dump(pw); pw.decreaseIndent(); } + + pw.println("mMhlController: "); + pw.increaseIndent(); + mMhlController.dump(pw); + pw.decreaseIndent(); + pw.println("mPortInfo: "); pw.increaseIndent(); for (HdmiPortInfo hdmiPortInfo : mPortInfo) { @@ -1849,8 +1955,7 @@ public final class HdmiControlService extends SystemService { void wakeUp() { assertRunOnServiceThread(); mWakeUpMessageReceived = true; - PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - pm.wakeUp(SystemClock.uptimeMillis()); + mPowerManager.wakeUp(SystemClock.uptimeMillis()); // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets // the intent, the sequence will continue at onWakeUp(). } @@ -1859,8 +1964,7 @@ public final class HdmiControlService extends SystemService { void standby() { assertRunOnServiceThread(); mStandbyMessageReceived = true; - PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0); + mPowerManager.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0); // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets // the intent, the sequence will continue at onStandby(). } @@ -1886,6 +1990,7 @@ public final class HdmiControlService extends SystemService { @ServiceThreadOnly private void onStandby() { assertRunOnServiceThread(); + if (!canGoToStandby()) return; mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; invokeVendorCommandListenersOnControlStateChanged(false, HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY); @@ -1906,6 +2011,13 @@ public final class HdmiControlService extends SystemService { }); } + private boolean canGoToStandby() { + for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { + if (!device.canGoToStandby()) return false; + } + return true; + } + @ServiceThreadOnly private void onLanguageChanged(String language) { assertRunOnServiceThread(); @@ -1913,6 +2025,7 @@ public final class HdmiControlService extends SystemService { if (isTvDeviceEnabled()) { tv().broadcastMenuLanguage(language); + mCecController.setOption(OPTION_CEC_SET_LANGUAGE, HdmiUtils.languageToInt(language)); } } @@ -1955,7 +2068,9 @@ public final class HdmiControlService extends SystemService { device.onStandby(mStandbyMessageReceived); } mStandbyMessageReceived = false; + mAddressAllocated = false; mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED); + mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED); } private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { @@ -2137,16 +2252,17 @@ public final class HdmiControlService extends SystemService { assertRunOnServiceThread(); if (tv() == null) return; final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID; - tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() { - @Override - public void onComplete(int result) throws RemoteException { - // Keep the last input to switch back later when RAP[ContentOff] is received. - // This effectively sets the port to invalid one if the switching is for - // RAP[ContentOff]. - setLastInputForMhl(lastInput); - } - }); - + if (portId != Constants.INVALID_PORT_ID) { + tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) throws RemoteException { + // Keep the last input to switch back later when RAP[ContentOff] is received. + // This effectively sets the port to invalid one if the switching is for + // RAP[ContentOff]. + setLastInputForMhl(lastInput); + } + }); + } // MHL device is always directly connected to the port. Update the active port ID to avoid // unnecessary post-routing control task. tv().setActivePortId(portId); @@ -2156,7 +2272,8 @@ public final class HdmiControlService extends SystemService { // may not be the MHL-enabled one. In this case the device info to be passed to // input change listener should be the one describing the corresponding HDMI port. HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId); - HdmiDeviceInfo info = (device != null) ? device.getInfo() : mPortDeviceMap.get(portId); + HdmiDeviceInfo info = (device != null) ? device.getInfo() + : mPortDeviceMap.get(portId, HdmiDeviceInfo.INACTIVE_DEVICE); invokeInputChangeListener(info); } diff --git a/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java b/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java index 708aee6..3883200 100644 --- a/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java +++ b/services/core/java/com/android/server/hdmi/HdmiMhlControllerStub.java @@ -19,6 +19,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiPortInfo; import android.util.SparseArray; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; /** @@ -135,4 +136,7 @@ final class HdmiMhlControllerStub { int getEcbusDeviceRoles(int portId) { return INVALID_DEVICE_ROLES; } + + void dump(IndentingPrintWriter pw) { + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java index 53a7c5c..06ecb5a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java +++ b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java @@ -43,4 +43,7 @@ final class HdmiMhlLocalDeviceStub { void sendKeyEvent(int keycode, boolean isPressed) { } + + void sendStandby() { + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index 22a519b..9aa9290 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -292,4 +292,17 @@ final class HdmiUtils { info.getVendorId(), info.getDisplayName(), newPowerStatus); } + /** + * Convert 3 byte-long language code in string to integer representation. + * English(eng), for example, is converted to 0x656e67. + * + * @param language language code in string + * @return language code in integer representation + */ + static int languageToInt(String language) { + String normalized = language.toLowerCase(); + return ((normalized.charAt(0) & 0xFF) << 16) + | ((normalized.charAt(1) & 0xFF) << 8) + | (normalized.charAt(2) & 0xFF); + } } diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index 722be71..1bbd038 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -38,6 +38,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction { private static final int POLLING_INTERVAL_MS = 5000; private static final int TIMEOUT_COUNT = 3; + private static final int AVR_COUNT_MAX = 3; // State in which waits for next polling private static final int STATE_WAIT_FOR_NEXT_POLLING = 1; @@ -48,6 +49,12 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction { private int mTimeoutCount = 0; + // Counter used to ensure the connection to AVR is stable. Occasional failure to get + // polling response from AVR despite its presence leads to unstable status flipping. + // This is a workaround to deal with it, by removing the device only if the removal + // is detected {@code AVR_COUNT_MAX} times in a row. + private int mAvrStatusCount = 0; + /** * Constructor * @@ -148,10 +155,22 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction { BitSet removed = complement(currentInfos, polledResult); int index = -1; while ((index = removed.nextSetBit(index + 1)) != -1) { + if (index == Constants.ADDR_AUDIO_SYSTEM) { + ++mAvrStatusCount; + Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount); + if (mAvrStatusCount < AVR_COUNT_MAX) { + continue; + } + } Slog.v(TAG, "Remove device by hot-plug detection:" + index); removeDevice(index); } + // Reset the counter if the ack is returned from AVR. + if (!removed.get(Constants.ADDR_AUDIO_SYSTEM)) { + mAvrStatusCount = 0; + } + // Next, check added devices. BitSet added = complement(polledResult, currentInfos); index = -1; diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index 3d64cc5..6753368 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -163,6 +163,12 @@ final class NewDeviceAction extends HdmiCecFeatureAction { } private void addDeviceInfo() { + // The device should be in the device list with default information. + if (!tv().isInDeviceList(mDeviceLogicalAddress, mDevicePhysicalAddress)) { + Slog.w(TAG, String.format("Device not found (%02x, %04x)", + mDeviceLogicalAddress, mDevicePhysicalAddress)); + return; + } if (mDisplayName == null) { mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress); } diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java index e764a1c..a711102 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java @@ -82,7 +82,7 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { private void broadcastActiveSource() { sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath())); // Because only playback device can create this action, it's safe to cast. - playback().markActiveSource(); + playback().setActiveSource(true); } private void queryDevicePowerStatus() { diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java index 435ab7f..ce5b9ab 100644 --- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java +++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java @@ -119,10 +119,9 @@ final class RoutingControlAction extends HdmiCecFeatureAction { private void handleReportPowerStatus(int devicePowerStatus) { if (isPowerOnOrTransient(getTvPowerStatus())) { + tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange); if (isPowerOnOrTransient(devicePowerStatus)) { sendSetStreamPath(); - } else { - tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange); } } finishWithCallback(HdmiControlManager.RESULT_SUCCESS); diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java index eef5010..40d2583 100644 --- a/services/core/java/com/android/server/hdmi/SendKeyAction.java +++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java @@ -35,14 +35,25 @@ import android.view.KeyEvent; final class SendKeyAction extends HdmiCecFeatureAction { private static final String TAG = "SendKeyAction"; + // If the first key press lasts this much amount of time without any other key event + // coming down, we trigger the press-and-hold operation. Set to the value slightly + // shorter than the threshold(500ms) between two successive key press events + // as specified in the standard for the operation. + private static final int AWAIT_LONGPRESS_MS = 400; + // Amount of time this action waits for a new release key input event. When timed out, // the action sends out UCR and finishes its lifecycle. Used to deal with missing key release // event, which can lead the device on the receiving end to generating unintended key repeats. private static final int AWAIT_RELEASE_KEY_MS = 1000; - // State in which the action is at work. The state is set in {@link #start()} and - // persists throughout the process till it is set back to {@code STATE_NONE} at the end. - private static final int STATE_PROCESSING_KEYCODE = 1; + // State in which the long press is being checked at the beginning. The state is set in + // {@link #start()} and lasts for {@link #AWAIT_LONGPRESS_MS}. + private static final int STATE_CHECKING_LONGPRESS = 1; + + // State in which the action is handling incoming keys. Persists throughout the process + // till it is set back to {@code STATE_NONE} at the end when a release key event for + // the last key is processed. + private static final int STATE_PROCESSING_KEYCODE = 2; // Logical address of the device to which the UCP/UCP commands are sent. private final int mTargetAddress; @@ -77,8 +88,8 @@ final class SendKeyAction extends HdmiCecFeatureAction { finish(); return true; } - mState = STATE_PROCESSING_KEYCODE; - addTimer(mState, AWAIT_RELEASE_KEY_MS); + mState = STATE_CHECKING_LONGPRESS; + addTimer(mState, AWAIT_LONGPRESS_MS); return true; } @@ -93,7 +104,7 @@ final class SendKeyAction extends HdmiCecFeatureAction { * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN} */ void processKeyEvent(int keycode, boolean isPressed) { - if (mState != STATE_PROCESSING_KEYCODE) { + if (mState != STATE_CHECKING_LONGPRESS && mState != STATE_PROCESSING_KEYCODE) { Slog.w(TAG, "Not in a valid state"); return; } @@ -152,12 +163,23 @@ final class SendKeyAction extends HdmiCecFeatureAction { @Override public void handleTimerEvent(int state) { - // Timeout on waiting for the release key event. Send UCR and quit the action. - if (mState != STATE_PROCESSING_KEYCODE) { - Slog.w(TAG, "Not in a valid state"); - return; + switch (mState) { + case STATE_CHECKING_LONGPRESS: + // The first key press lasts long enough to start press-and-hold. + mActionTimer.clearTimerMessage(); + mState = STATE_PROCESSING_KEYCODE; + sendKeyDown(mLastKeycode); + mLastSendKeyTime = getCurrentTime(); + addTimer(mState, AWAIT_RELEASE_KEY_MS); + break; + case STATE_PROCESSING_KEYCODE: + // Timeout on waiting for the release key event. Send UCR and quit the action. + sendKeyUp(); + finish(); + break; + default: + Slog.w(TAG, "Not in a valid state"); + break; } - sendKeyUp(); - finish(); } } diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index 9c0f987..5ae6300 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -648,7 +648,7 @@ public class GpsLocationProvider implements LocationProviderInterface { // Register for SubscriptionInfo list changes which is guaranteed // to invoke onSubscriptionsChanged the first time. SubscriptionManager.from(mContext) - .registerOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); + .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); // construct handler, listen for events mHandler = new ProviderHandler(looper); @@ -788,9 +788,7 @@ public class GpsLocationProvider implements LocationProviderInterface { } if (info != null) { - boolean dataEnabled = TelephonyManager.getIntWithSubId(mContext.getContentResolver(), - Settings.Global.MOBILE_DATA, SubscriptionManager.getDefaultSubId(), - 1) == 1; + boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled(); boolean networkAvailable = info.isAvailable() && dataEnabled; String defaultApn = getSelectedApn(); if (defaultApn == null) { diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 1fba7bb..53ae1ab 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -93,7 +93,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final MediaSessionService mService; private final boolean mUseMasterVolume; - private final IBinder mICallback = new Binder(); private final Object mLock = new Object(); private final ArrayList<ISessionControllerCallback> mControllerCallbacks = new ArrayList<ISessionControllerCallback>(); @@ -260,13 +259,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { boolean isMasterMute = mAudioManager.isMasterMute(); if (isMute) { mAudioManagerInternal.setMasterMuteForUid(!isMasterMute, - flags, packageName, mICallback, uid); + flags, packageName, mService.mICallback, uid); } else { mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName, uid); if (isMasterMute) { mAudioManagerInternal.setMasterMuteForUid(false, - flags, packageName, mICallback, uid); + flags, packageName, mService.mICallback, uid); } } return; @@ -280,7 +279,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } else { mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction, flags, packageName, uid); - if (isStreamMute) { + if (isStreamMute && direction != 0) { mAudioManager.setStreamMute(stream, false); } } @@ -295,7 +294,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mAudioManagerInternal.adjustSuggestedStreamVolumeForUid( AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName, uid); - if (isStreamMute) { + if (isStreamMute && direction != 0) { mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE, false); } @@ -307,7 +306,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } else { mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, packageName, uid); - if (isStreamMute) { + if (isStreamMute && direction != 0) { mAudioManager.setStreamMute(stream, false); } } @@ -331,6 +330,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { if (volumeBefore != mOptimisticVolume) { pushVolumeUpdate(); } + mService.notifyRemoteVolumeChanged(flags, this); if (DEBUG) { Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is " @@ -358,6 +358,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { if (volumeBefore != mOptimisticVolume) { pushVolumeUpdate(); } + mService.notifyRemoteVolumeChanged(flags, this); if (DEBUG) { Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is " diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 82be1bb..77a1fa9 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -77,6 +77,8 @@ public class MediaSessionService extends SystemService implements Monitor { private static final int WAKELOCK_TIMEOUT = 5000; + /* package */final IBinder mICallback = new Binder(); + private final SessionManagerImpl mSessionManagerImpl; private final MediaSessionStack mPriorityStack; @@ -91,6 +93,7 @@ public class MediaSessionService extends SystemService implements Monitor { private KeyguardManager mKeyguardManager; private IAudioService mAudioService; + private AudioManager mAudioManager; private ContentResolver mContentResolver; private SettingsObserver mSettingsObserver; @@ -118,6 +121,7 @@ public class MediaSessionService extends SystemService implements Monitor { mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); mAudioService = getAudioService(); + mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mContentResolver = getContext().getContentResolver(); mSettingsObserver = new SettingsObserver(); mSettingsObserver.observe(); @@ -139,6 +143,20 @@ public class MediaSessionService extends SystemService implements Monitor { mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); } + /** + * Tells the system UI that volume has changed on a remote session. + */ + public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { + if (mRvc == null) { + return; + } + try { + mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); + } catch (Exception e) { + Log.wtf(TAG, "Error sending volume change to system UI.", e); + } + } + public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { boolean updateSessions = false; synchronized (mLock) { @@ -589,8 +607,6 @@ public class MediaSessionService extends SystemService implements Monitor { "android.media.AudioService.WAKELOCK_ACQUIRED"; private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number - private final IBinder mICallback = new Binder(); - private boolean mVoiceButtonDown = false; private boolean mVoiceButtonHandled = false; @@ -836,19 +852,23 @@ public class MediaSessionService extends SystemService implements Monitor { mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback); } else { mAudioService.adjustMasterVolume(direction, flags, packageName); - if (isMasterMute) { + // Do not call setMasterMute when direction = 0 which is used just to + // show the UI. + if (isMasterMute && direction != 0) { mAudioService.setMasterMute(false, flags, packageName, mICallback); } } } else { boolean isStreamMute = mAudioService.isStreamMute(suggestedStream); if (direction == MediaSessionManager.DIRECTION_MUTE) { - mAudioService.setStreamMute(suggestedStream, !isStreamMute, mICallback); + mAudioManager.setStreamMute(suggestedStream, !isStreamMute); } else { mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags, packageName); - if (isStreamMute) { - mAudioService.setStreamMute(suggestedStream, false, mICallback); + // Do not call setStreamMute when direction = 0 which is used just to + // show the UI. + if (isStreamMute && direction != 0) { + mAudioManager.setStreamMute(suggestedStream, false); } } } @@ -858,14 +878,6 @@ public class MediaSessionService extends SystemService implements Monitor { } else { session.adjustVolume(direction, flags, getContext().getPackageName(), UserHandle.myUserId(), true); - if (session.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE - && mRvc != null && direction != MediaSessionManager.DIRECTION_MUTE) { - try { - mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); - } catch (Exception e) { - Log.wtf(TAG, "Error sending volume change to system UI.", e); - } - } } } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index e9b3f8b..bfdc400 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -98,6 +98,11 @@ public final class MediaProjectionManagerService extends SystemService @Override public void onSwitchUser(int userId) { mMediaRouter.rebindAsUser(userId); + synchronized (mLock) { + if (mProjectionGrant != null) { + mProjectionGrant.stop(); + } + } } @Override diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 3a1e4a4..752614f 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -140,7 +140,7 @@ public class LockdownVpnTracker { if (egressDisconnected || egressChanged) { clearSourceRulesLocked(); mAcceptedEgressIface = null; - mVpn.stopLegacyVpn(); + mVpn.stopLegacyVpnPrivileged(); } if (egressDisconnected) { hideNotification(); @@ -163,7 +163,9 @@ public class LockdownVpnTracker { mAcceptedEgressIface = egressProp.getInterfaceName(); try { - mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp); + // Use the privileged method because Lockdown VPN is initiated by the system, so + // no additional permission checks are necessary. + mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp); } catch (IllegalStateException e) { mAcceptedEgressIface = null; Slog.e(TAG, "Failed to start VPN", e); @@ -250,7 +252,7 @@ public class LockdownVpnTracker { mAcceptedEgressIface = null; mErrorCount = 0; - mVpn.stopLegacyVpn(); + mVpn.stopLegacyVpnPrivileged(); try { mNetService.setFirewallEgressDestRule(mProfile.server, 500, false); mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false); diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 64d77c1..ab53fbc 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -168,7 +168,6 @@ public class ConditionProviders extends ManagedServices { @Override protected void onServiceAdded(ManagedServiceInfo info) { - Slog.d(TAG, "onServiceAdded " + info); final IConditionProvider provider = provider(info); try { provider.onConnected(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 825627f..f49d77d 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -683,6 +683,9 @@ public class NotificationManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + if (action == null) { + return; + } boolean queryRestart = false; boolean queryRemove = false; @@ -928,6 +931,7 @@ public class NotificationManagerService extends SystemService { mDisableNotificationEffects = true; } mZenModeHelper.readZenModeFromSetting(); + mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); mUserProfiles.updateCache(getContext()); listenForCallState(); @@ -1668,6 +1672,7 @@ public class NotificationManagerService extends SystemService { if (filter == null || zenOnly) { pw.println("\n Zen Mode:"); + pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter); mZenModeHelper.dump(pw, " "); pw.println("\n Zen Log:"); @@ -1829,7 +1834,7 @@ public class NotificationManagerService extends SystemService { // can to avoid extracting signals. handleGroupedNotificationLocked(r, old, callingUid, callingPid); boolean ignoreNotification = - removeUnusedGroupedNotificationLocked(r, callingUid, callingPid); + removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid); // This conditional is a dirty hack to limit the logging done on // behalf of the download manager without affecting other apps. @@ -1966,7 +1971,8 @@ public class NotificationManagerService extends SystemService { * * <p>Returns true if the given notification is a child of a group with a * summary, which means that SysUI will never show it, and hence the new - * notification can be safely ignored.</p> + * notification can be safely ignored. Also cancels any previous instance + * of the ignored notification.</p> * * <p>For summaries, cancels all children of that group, as SysUI will * never show them anymore.</p> @@ -1974,7 +1980,7 @@ public class NotificationManagerService extends SystemService { * @return true if the given notification can be ignored as an optimization */ private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r, - int callingUid, int callingPid) { + NotificationRecord old, int callingUid, int callingPid) { // No optimizations are possible if listeners want groups. if (mListeners.notificationGroupsDesired()) { return false; @@ -1992,6 +1998,13 @@ public class NotificationManagerService extends SystemService { Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary " + summary.getKey()); } + // Make sure we don't leave an old version of the notification around. + if (old != null) { + if (DBG) { + Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey()); + } + cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION); + } return true; } else if (isSummary) { // Summary -> cancel children diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 91637e3..cc0a30a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3281,7 +3281,7 @@ public class PackageManagerService extends IPackageManager.Stub { // If the result set is different from when this // was created, we need to clear it and re-ask the // user their preference, if we're looking for an "always" type entry. - if (always && !pa.mPref.sameSet(query, priority)) { + if (always && !pa.mPref.sameSet(query)) { Slog.i(TAG, "Result set changed, dropping preferred activity for " + intent + " type " + resolvedType); if (DEBUG_PREFERRED) { @@ -8636,7 +8636,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_INSTALL) Log.v(TAG, "token " + token + " to BM for possible restore"); try { - bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); + if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) { + bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); + } else { + doRestore = false; + } } catch (RemoteException e) { // can't happen; the backup manager is local } catch (Exception e) { diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index 69c1909..8e2e0cd 100644 --- a/services/core/java/com/android/server/pm/PreferredComponent.java +++ b/services/core/java/com/android/server/pm/PreferredComponent.java @@ -192,7 +192,7 @@ public class PreferredComponent { } } - public boolean sameSet(List<ResolveInfo> query, int priority) { + public boolean sameSet(List<ResolveInfo> query) { if (mSetPackages == null) { return query == null; } @@ -201,10 +201,10 @@ public class PreferredComponent { } final int NQ = query.size(); final int NS = mSetPackages.length; + int numMatch = 0; for (int i=0; i<NQ; i++) { ResolveInfo ri = query.get(i); - if (ri.priority != priority) continue; ActivityInfo ai = ri.activityInfo; boolean good = false; for (int j=0; j<NS; j++) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index be3251c..d484b8f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -320,6 +320,9 @@ public class UserManagerService extends IUserManager.Stub { checkManageUsersPermission("get the profile parent"); synchronized (mPackagesLock) { UserInfo profile = getUserInfoLocked(userHandle); + if (profile == null) { + return null; + } int parentUserId = profile.profileGroupId; if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) { return null; diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java index 64a67fc..22fee22 100644 --- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java +++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java @@ -63,7 +63,7 @@ public class TelecomLoaderService extends SystemService { private static final ComponentName SERVICE_COMPONENT = new ComponentName( "com.android.server.telecom", - "com.android.server.telecom.TelecomService"); + "com.android.server.telecom.components.TelecomService"); private static final String SERVICE_ACTION = "com.android.ITelecomService"; diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index c12dd63..de271b8 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -20,6 +20,7 @@ import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvStreamConfig; import android.os.Handler; import android.os.Message; +import android.os.MessageQueue; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -53,7 +54,7 @@ final class TvInputHal implements Handler.Callback { public void onFirstFrameCaptured(int deviceId, int streamId); } - private native long nativeOpen(); + private native long nativeOpen(MessageQueue queue); private static native int nativeAddOrUpdateStream(long ptr, int deviceId, int streamId, Surface surface); @@ -76,7 +77,7 @@ final class TvInputHal implements Handler.Callback { public void init() { synchronized (mLock) { - mPtr = nativeOpen(); + mPtr = nativeOpen(mHandler.getLooper().getQueue()); } } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 7786034..50b2262 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -216,10 +216,13 @@ class TvInputHardwareManager implements TvInputHal.Callback { mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget(); } - try { - connection.getCallbackLocked().onStreamConfigChanged(configs); - } catch (RemoteException e) { - Slog.e(TAG, "error in onStreamConfigurationChanged", e); + ITvInputHardwareCallback callback = connection.getCallbackLocked(); + if (callback != null) { + try { + callback.onStreamConfigChanged(configs); + } catch (RemoteException e) { + Slog.e(TAG, "error in onStreamConfigurationChanged", e); + } } } } @@ -664,7 +667,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { public void onServiceDied() { synchronized (mImplLock) { mAudioSource = null; - mAudioSink = null; + mAudioSink.clear(); mAudioPatch = null; } } @@ -672,9 +675,11 @@ class TvInputHardwareManager implements TvInputHal.Callback { private int mOverrideAudioType = AudioManager.DEVICE_NONE; private String mOverrideAudioAddress = ""; private AudioDevicePort mAudioSource; - private AudioDevicePort mAudioSink; + private List<AudioDevicePort> mAudioSink = new ArrayList<>(); private AudioPatch mAudioPatch = null; - private float mCommittedVolume = 0.0f; + // Set to an invalid value for a volume, so that current volume can be applied at the + // first call to updateAudioConfigLocked(). + private float mCommittedVolume = -1f; private float mSourceVolume = 0.0f; private TvStreamConfig mActiveConfig = null; @@ -688,22 +693,23 @@ class TvInputHardwareManager implements TvInputHal.Callback { mAudioManager.registerAudioPortUpdateListener(mAudioListener); if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) { mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress()); - mAudioSink = findAudioSinkFromAudioPolicy(); + findAudioSinkFromAudioPolicy(mAudioSink); } } - private AudioDevicePort findAudioSinkFromAudioPolicy() { + private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) { + sinks.clear(); ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>(); - if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) { - int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); - for (AudioPort port : devicePorts) { - AudioDevicePort devicePort = (AudioDevicePort) port; - if ((devicePort.type() & sinkDevice) != 0) { - return devicePort; - } + if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) { + return; + } + int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); + for (AudioPort port : devicePorts) { + AudioDevicePort devicePort = (AudioDevicePort) port; + if ((devicePort.type() & sinkDevice) != 0) { + sinks.add(devicePort); } } - return null; } private AudioDevicePort findAudioDevicePort(int type, String address) { @@ -789,7 +795,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here // because Java won't evaluate the latter if the former is true. - if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) { + if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) { if (mAudioPatch != null) { mAudioManager.releaseAudioPatch(mAudioPatch); mAudioPatch = null; @@ -828,45 +834,53 @@ class TvInputHardwareManager implements TvInputHal.Callback { } AudioPortConfig sourceConfig = mAudioSource.activeConfig(); - AudioPortConfig sinkConfig = mAudioSink.activeConfig(); + List<AudioPortConfig> sinkConfigs = new ArrayList<>(); 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(); + for (AudioDevicePort audioSink : mAudioSink) { + AudioPortConfig sinkConfig = audioSink.activeConfig(); + 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 - || 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; + if (sinkConfig == null + || sinkConfig.samplingRate() != sinkSamplingRate + || sinkConfig.channelMask() != sinkChannelMask + || sinkConfig.format() != sinkFormat) { + // Check for compatibility and reset to default if necessary. + if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate) + && audioSink.samplingRates().length > 0) { + sinkSamplingRate = audioSink.samplingRates()[0]; + } + if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) { + sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT; + } + if (!intArrayContains(audioSink.formats(), sinkFormat)) { + sinkFormat = AudioFormat.ENCODING_DEFAULT; + } + sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask, + sinkFormat, null); + shouldRecreateAudioPatch = true; } - sinkConfig = mAudioSink.buildConfig(sinkSamplingRate, sinkChannelMask, - sinkFormat, null); - shouldRecreateAudioPatch = true; + sinkConfigs.add(sinkConfig); } + // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be + // non-empty at the beginning of this method. + AudioPortConfig sinkConfig = sinkConfigs.get(0); if (sourceConfig == null || sourceGainConfig != null) { int sourceSamplingRate = 0; if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) { @@ -896,7 +910,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { mAudioManager.createAudioPatch( audioPatchArray, new AudioPortConfig[] { sourceConfig }, - new AudioPortConfig[] { sinkConfig }); + sinkConfigs.toArray(new AudioPortConfig[0])); mAudioPatch = audioPatchArray[0]; if (sourceGainConfig != null) { mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig); @@ -974,17 +988,24 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) { return false; } - AudioDevicePort previousSink = mAudioSink; + List<AudioDevicePort> previousSink = mAudioSink; + mAudioSink = new ArrayList<>(); if (mOverrideAudioType == AudioManager.DEVICE_NONE) { - mAudioSink = findAudioSinkFromAudioPolicy(); + findAudioSinkFromAudioPolicy(mAudioSink); } else { AudioDevicePort audioSink = findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress); if (audioSink != null) { - mAudioSink = audioSink; + mAudioSink.add(audioSink); } } - return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink); + + // Returns true if mAudioSink and previousSink differs. + if (mAudioSink.size() != previousSink.size()) { + return true; + } + previousSink.removeAll(mAudioSink); + return !previousSink.isEmpty(); } private void handleAudioSinkUpdated() { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index f859fd2..da25c53 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -304,10 +304,11 @@ class AppWindowToken extends WindowToken { pw.print(prefix); pw.print("inPendingTransaction="); pw.println(inPendingTransaction); } - if (startingData != null || removed || firstWindowDrawn) { + if (startingData != null || removed || firstWindowDrawn || mDeferRemoval) { pw.print(prefix); pw.print("startingData="); pw.print(startingData); pw.print(" removed="); pw.print(removed); - pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn); + pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn); + pw.print(" mDeferRemoval="); pw.println(mDeferRemoval); } if (startingWindow != null || startingView != null || startingDisplayed || startingMoved) { diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java index a7d41fa..9fdfc47 100644 --- a/services/core/java/com/android/server/wm/CircularDisplayMask.java +++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.view.Display; import android.view.Surface; @@ -35,7 +36,6 @@ import android.util.Slog; class CircularDisplayMask { private static final String TAG = "CircularDisplayMask"; - private static final int STROKE_WIDTH = 2; // size of the chin private int mScreenOffset = 0; // Display dimensions @@ -82,9 +82,7 @@ class CircularDisplayMask { mDrawNeeded = true; mPaint = new Paint(); mPaint.setAntiAlias(true); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setColor(Color.BLACK); - mPaint.setStrokeWidth(STROKE_WIDTH); + mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); mScreenOffset = screenOffset; } @@ -104,7 +102,6 @@ class CircularDisplayMask { if (c == null) { return; } - c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC); switch (mRotation) { case Surface.ROTATION_0: case Surface.ROTATION_90: @@ -122,7 +119,10 @@ class CircularDisplayMask { } int circleRadius = mScreenSize.x / 2; - c.drawCircle(circleRadius, circleRadius, circleRadius, mPaint); + c.drawColor(Color.BLACK); + + // The radius is reduced by 1 to provide an anti aliasing effect on the display edges. + c.drawCircle(circleRadius, circleRadius, circleRadius - 1, mPaint); mSurface.unlockCanvasAndPost(c); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index b0feca8..f57adaf 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -379,14 +379,18 @@ class DisplayContent { pw.println(" Application tokens in top down Z order:"); int ndx = 0; for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { - ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks(); + final TaskStack stack = mStacks.get(stackNdx); + pw.print(" mStackId="); pw.println(stack.mStackId); + ArrayList<Task> tasks = stack.getTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { - AppTokenList tokens = tasks.get(taskNdx).mAppTokens; - for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final Task task = tasks.get(taskNdx); + pw.print(" mTaskId="); pw.println(task.taskId); + AppTokenList tokens = task.mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) { final AppWindowToken wtoken = tokens.get(tokenNdx); - pw.print(" App #"); pw.print(ndx++); + pw.print(" Activity #"); pw.print(tokenNdx); pw.print(' '); pw.print(wtoken); pw.println(":"); - wtoken.dump(pw, " "); + wtoken.dump(pw, " "); } } } diff --git a/services/core/java/com/android/server/wm/StackTapPointerEventListener.java b/services/core/java/com/android/server/wm/StackTapPointerEventListener.java index 19d8ab3..8938358 100644 --- a/services/core/java/com/android/server/wm/StackTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/StackTapPointerEventListener.java @@ -57,6 +57,7 @@ public class StackTapPointerEventListener implements PointerEventListener { if (mPointerId >= 0) { int index = motionEvent.findPointerIndex(mPointerId); if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC + || index < 0 || (motionEvent.getX(index) - mDownX) > mMotionSlop || (motionEvent.getY(index) - mDownY) > mMotionSlop) { mPointerId = -1; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a60be3b..b49b87c 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -68,6 +68,6 @@ class Task { @Override public String toString() { - return "{taskId=" + taskId + " appTokens=" + mAppTokens + "}"; + return "{taskId=" + taskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}"; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2750941..089d897 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -262,6 +262,8 @@ public class WindowManagerService extends IWindowManager.Stub /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */ static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000; + /** Amount of time to allow a last ANR message to exist before freeing the memory. */ + static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours /** * If true, the window manager will do its own custom freezing and general * management of the screen during rotation. @@ -762,7 +764,7 @@ public class WindowManagerService extends IWindowManager.Stub * Whether the UI is currently running in touch mode (not showing * navigational focus because the user is directly pressing the screen). */ - boolean mInTouchMode = true; + boolean mInTouchMode; private ViewServer mViewServer; private final ArrayList<WindowChangeListener> mWindowChangeListeners = @@ -823,6 +825,8 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_sf_limitedAlpha); mHasPermanentDpad = context.getResources().getBoolean( com.android.internal.R.bool.config_hasPermanentDpad); + mInTouchMode = context.getResources().getBoolean( + com.android.internal.R.bool.config_defaultInTouchMode); mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplaySettings = new DisplaySettings(); @@ -940,7 +944,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowAfter(WindowState pos, WindowState window) { final WindowList windows = pos.getWindowList(); final int i = windows.indexOf(pos); - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( + if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " + (i+1) + " of " + windows.size() + " (after " + pos + ")"); windows.add(i+1, window); @@ -950,7 +954,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowBefore(WindowState pos, WindowState window) { final WindowList windows = pos.getWindowList(); int i = windows.indexOf(pos); - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( + if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " + i + " of " + windows.size() + " (before " + pos + ")"); if (i < 0) { @@ -1048,7 +1052,7 @@ public class WindowManagerService extends IWindowManager.Stub //apptoken note that the window could be a floating window //that was created later or a window at the top of the list of //windows associated with this token. - if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " + N); windows.add(newIdx + 1, win); @@ -1164,15 +1168,15 @@ public class WindowManagerService extends IWindowManager.Stub // Just search for the start of this layer. final int myLayer = win.mBaseLayer; int i; - for (i = 0; i < N; i++) { + for (i = N - 1; i >= 0; --i) { WindowState w = windows.get(i); - if (w.mBaseLayer > myLayer) { + if (w.mBaseLayer <= myLayer) { break; } } - if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, - "Based on layer: Adding window " + win + " at " + i + " of " + N); - windows.add(i, win); + if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N); + windows.add(i + 1, win); mWindowsChanged = true; return tokenWindowsPos; } @@ -3657,7 +3661,7 @@ public class WindowManagerService extends IWindowManager.Stub atoken.layoutConfigChanges = (configChanges & (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0; atoken.mLaunchTaskBehind = launchTaskBehind; - if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken + if (true || DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken + " to stack=" + stackId + " task=" + taskId + " at " + addPos); Task task = mTaskIdToTask.get(taskId); @@ -4808,7 +4812,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken: " + wtoken + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); final TaskStack stack = mTaskIdToTask.get(wtoken.groupId).mStack; - if (delayed) { + if (delayed && !wtoken.allAppWindows.isEmpty()) { // set the token aside because it has an active animation to be finished if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken make exiting: " + wtoken); @@ -5192,7 +5196,7 @@ public class WindowManagerService extends IWindowManager.Stub void removeTaskLocked(Task task) { final int taskId = task.taskId; final TaskStack stack = task.mStack; - if (stack.isAnimating()) { + if (!task.mAppTokens.isEmpty() && stack.isAnimating()) { if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + taskId); task.mDeferRemoval = true; return; @@ -5200,8 +5204,17 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + taskId); EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, taskId, "removeTask"); task.mDeferRemoval = false; - task.mStack.removeTask(task); + stack.removeTask(task); mTaskIdToTask.delete(task.taskId); + + final ArrayList<AppWindowToken> exitingApps = stack.mExitingAppTokens; + for (int appNdx = exitingApps.size() - 1; appNdx >= 0; --appNdx) { + final AppWindowToken wtoken = exitingApps.get(appNdx); + if (wtoken.groupId == taskId) { + wtoken.mDeferRemoval = false; + exitingApps.remove(appNdx); + } + } } public void removeTask(int taskId) { @@ -5370,7 +5383,12 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean isKeyguardSecure() { - return mPolicy.isKeyguardSecure(); + long origId = Binder.clearCallingIdentity(); + try { + return mPolicy.isKeyguardSecure(); + } finally { + Binder.restoreCallingIdentity(origId); + } } @Override @@ -7599,6 +7617,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36; public static final int CHECK_IF_BOOT_ANIMATION_FINISHED = 37; + public static final int RESET_ANR_MESSAGE = 38; @Override public void handleMessage(Message msg) { @@ -8043,16 +8062,16 @@ public class WindowManagerService extends IWindowManager.Stub break; case TAP_OUTSIDE_STACK: { - int stackId; - synchronized (mWindowMap) { - stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2); - } - if (stackId >= 0) { - try { - mActivityManager.setFocusedStack(stackId); - } catch (RemoteException e) { - } - } +// int stackId; +// synchronized (mWindowMap) { +// stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2); +// } +// if (stackId >= 0) { +// try { +// mActivityManager.setFocusedStack(stackId); +// } catch (RemoteException e) { +// } +// } } break; case NOTIFY_ACTIVITY_DRAWN: @@ -8109,6 +8128,12 @@ public class WindowManagerService extends IWindowManager.Stub } } break; + case RESET_ANR_MESSAGE: { + synchronized (mWindowMap) { + mLastANRState = null; + } + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG, "handleMessage: exit"); @@ -10018,7 +10043,8 @@ public class WindowManagerService extends IWindowManager.Stub mStackIdToStack.valueAt(stackNdx).mExitingAppTokens; for (i = exitingAppTokens.size() - 1; i >= 0; i--) { AppWindowToken token = exitingAppTokens.get(i); - if (!token.hasVisible && !mClosingApps.contains(token) && !token.mDeferRemoval) { + if (!token.hasVisible && !mClosingApps.contains(token) && + (!token.mDeferRemoval || token.allAppWindows.isEmpty())) { // Make sure there is no animation running on this token, // so any windows associated with it will be removed as // soon as their animations are complete @@ -10028,6 +10054,10 @@ public class WindowManagerService extends IWindowManager.Stub "performLayout: App token exiting now removed" + token); removeAppFromTaskLocked(token); exitingAppTokens.remove(i); + final Task task = mTaskIdToTask.get(token.groupId); + if (task != null && task.mDeferRemoval && task.mAppTokens.isEmpty()) { + removeTaskLocked(task); + } } } } @@ -11290,8 +11320,14 @@ public class WindowManagerService extends IWindowManager.Stub } pw.println(); dumpWindowsNoHeaderLocked(pw, true, null); + pw.println(); + pw.println("Last ANR continued"); + dumpDisplayContentsLocked(pw, true); pw.close(); mLastANRState = sw.toString(); + + mH.removeMessages(H.RESET_ANR_MESSAGE); + mH.sendEmptyMessageDelayed(H.RESET_ANR_MESSAGE, LAST_ANR_LIFETIME_DURATION_MSECS); } @Override diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index 5cb0543..dcb5199 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -18,6 +18,7 @@ //#define LOG_NDEBUG 0 +#include "android_os_MessageQueue.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "JNIHelp.h" @@ -27,6 +28,7 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Log.h> +#include <utils/Looper.h> #include <utils/NativeHandle.h> #include <hardware/tv_input.h> @@ -233,12 +235,17 @@ class JTvInputHal { public: ~JTvInputHal(); - static JTvInputHal* createInstance(JNIEnv* env, jobject thiz); + static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper); int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface); int removeStream(int deviceId, int streamId); const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs); + void onDeviceAvailable(const tv_input_device_info_t& info); + void onDeviceUnavailable(int deviceId); + void onStreamConfigurationsChanged(int deviceId); + void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded); + private: // Connection between a surface and a stream. class Connection { @@ -254,28 +261,41 @@ private: sp<BufferProducerThread> mThread; }; - JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev); + class NotifyHandler : public MessageHandler { + public: + NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event); + ~NotifyHandler(); + + virtual void handleMessage(const Message& message); + + private: + tv_input_event_t mEvent; + JTvInputHal* mHal; + }; + + JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev, const sp<Looper>& looper); static void notify( tv_input_device_t* dev, tv_input_event_t* event, void* data); - void onDeviceAvailable(const tv_input_device_info_t& info); - void onDeviceUnavailable(int deviceId); - void onStreamConfigurationsChanged(int deviceId); - void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded); + static void cloneTvInputEvent( + tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent); Mutex mLock; jweak mThiz; tv_input_device_t* mDevice; tv_input_callback_ops_t mCallback; + sp<Looper> mLooper; KeyedVector<int, KeyedVector<int, Connection> > mConnections; }; -JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) { +JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device, + const sp<Looper>& looper) { mThiz = env->NewWeakGlobalRef(thiz); mDevice = device; mCallback.notify = &JTvInputHal::notify; + mLooper = looper; mDevice->initialize(mDevice, &mCallback, this); } @@ -288,7 +308,7 @@ JTvInputHal::~JTvInputHal() { mThiz = NULL; } -JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) { +JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) { tv_input_module_t* module = NULL; status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID, (hw_module_t const**)&module); @@ -309,7 +329,7 @@ JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) { return 0; } - return new JTvInputHal(env, thiz, device); + return new JTvInputHal(env, thiz, device, looper); } int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) { @@ -428,30 +448,20 @@ const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numCo void JTvInputHal::notify( tv_input_device_t* dev, tv_input_event_t* event, void* data) { JTvInputHal* thiz = (JTvInputHal*)data; - switch (event->type) { - case TV_INPUT_EVENT_DEVICE_AVAILABLE: { - thiz->onDeviceAvailable(event->device_info); - } break; - case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: { - thiz->onDeviceUnavailable(event->device_info.device_id); - } break; - case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: { - thiz->onStreamConfigurationsChanged(event->device_info.device_id); - } break; - case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: { - thiz->onCaptured(event->capture_result.device_id, - event->capture_result.stream_id, - event->capture_result.seq, - true /* succeeded */); - } break; - case TV_INPUT_EVENT_CAPTURE_FAILED: { - thiz->onCaptured(event->capture_result.device_id, - event->capture_result.stream_id, - event->capture_result.seq, - false /* succeeded */); - } break; - default: - ALOGE("Unrecognizable event"); + thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type); +} + +// static +void JTvInputHal::cloneTvInputEvent( + tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent) { + memcpy(dstEvent, srcEvent, sizeof(tv_input_event_t)); + if ((srcEvent->type == TV_INPUT_EVENT_DEVICE_AVAILABLE || + srcEvent->type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE || + srcEvent->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) && + srcEvent->device_info.audio_address != NULL){ + char* audio_address = new char[strlen(srcEvent->device_info.audio_address) + 1]; + strcpy(audio_address, srcEvent->device_info.audio_address); + dstEvent->device_info.audio_address = audio_address; } } @@ -549,10 +559,54 @@ void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succ } } +JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) { + mHal = hal; + cloneTvInputEvent(&mEvent, event); +} + +JTvInputHal::NotifyHandler::~NotifyHandler() { + if ((mEvent.type == TV_INPUT_EVENT_DEVICE_AVAILABLE || + mEvent.type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE || + mEvent.type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) && + mEvent.device_info.audio_address != NULL) { + delete mEvent.device_info.audio_address; + } +} + +void JTvInputHal::NotifyHandler::handleMessage(const Message& message) { + switch (mEvent.type) { + case TV_INPUT_EVENT_DEVICE_AVAILABLE: { + mHal->onDeviceAvailable(mEvent.device_info); + } break; + case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: { + mHal->onDeviceUnavailable(mEvent.device_info.device_id); + } break; + case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: { + mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id); + } break; + case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: { + mHal->onCaptured(mEvent.capture_result.device_id, + mEvent.capture_result.stream_id, + mEvent.capture_result.seq, + true /* succeeded */); + } break; + case TV_INPUT_EVENT_CAPTURE_FAILED: { + mHal->onCaptured(mEvent.capture_result.device_id, + mEvent.capture_result.stream_id, + mEvent.capture_result.seq, + false /* succeeded */); + } break; + default: + ALOGE("Unrecognizable event"); + } +} + //////////////////////////////////////////////////////////////////////////////// -static jlong nativeOpen(JNIEnv* env, jobject thiz) { - return (jlong)JTvInputHal::createInstance(env, thiz); +static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) { + sp<MessageQueue> messageQueue = + android_os_MessageQueue_getMessageQueue(env, messageQueueObj); + return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper()); } static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz, @@ -610,7 +664,7 @@ static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { static JNINativeMethod gTvInputHalMethods[] = { /* name, signature, funcPtr */ - { "nativeOpen", "()J", + { "nativeOpen", "(Landroid/os/MessageQueue;)J", (void*) nativeOpen }, { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I", (void*) nativeAddOrUpdateStream }, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5e405ce..40e2056 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -60,6 +60,7 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Environment; +import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.PersistableBundle; @@ -989,6 +990,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + /** + * Creates and loads the policy data from xml for data that is shared between + * various profiles of a user. In contrast to {@link #getUserData(int)} + * it allows access to data of users other than the calling user. + * + * This function should only be used for shared data, e.g. everything regarding + * passwords and should be removed once multiple screen locks are present. + * @param userHandle the user for whom to load the policy data + * @return + */ + DevicePolicyData getUserDataUnchecked(int userHandle) { + long ident = Binder.clearCallingIdentity(); + try { + return getUserData(userHandle); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + void removeUserData(int userHandle) { synchronized (this) { if (userHandle == UserHandle.USER_OWNER) { @@ -1348,6 +1368,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, "policies"); out.endDocument(); + stream.flush(); + FileUtils.sync(stream); stream.close(); journal.commit(); sendChangedNotification(userHandle); @@ -1694,7 +1716,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int smallIconId; String contentText; final String ownerName = getDeviceOwnerName(); - if (ownerName != null) { + if (isManagedProfile(userHandle.getIdentifier())) { + contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_administrator); + smallIconId = R.drawable.stat_sys_certificate_info; + } else if (ownerName != null) { contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed, ownerName); smallIconId = R.drawable.stat_sys_certificate_info; } else { @@ -1924,7 +1949,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -1971,7 +1996,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2018,7 +2043,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2079,7 +2104,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2177,7 +2202,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2234,7 +2259,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2278,7 +2303,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2325,7 +2350,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2372,7 +2397,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2419,7 +2444,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2466,7 +2491,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -2491,8 +2516,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // If the user this is called from is part of a profile group, that is the parent // of the group. UserInfo parent = getProfileParent(userHandle); - int id = parent == null ? userHandle : parent.id; - DevicePolicyData policy = getUserData(id); + int id = (parent == null) ? userHandle : parent.id; + DevicePolicyData policy = getUserDataUnchecked(id); // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. @@ -2522,7 +2547,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); // The active password is stored in the parent. - DevicePolicyData policy = getUserData(getProfileParent(userHandle).id); + UserInfo parent = getProfileParent(userHandle); + int id = (parent == null) ? userHandle : parent.id; + DevicePolicyData policy = getUserDataUnchecked(id); return policy.mFailedPasswordAttempts; } @@ -2585,7 +2612,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int count = 0; ActiveAdmin strictestAdmin = null; for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); for (ActiveAdmin admin : policy.mAdminList) { if (admin.maximumFailedPasswordsForWipe == ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) { @@ -2798,7 +2825,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Return strictest policy for this user and profiles that are visible from this user. List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); @@ -3022,7 +3049,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mHandler.post(new Runnable() { public void run() { try { - ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER); + IActivityManager am = ActivityManagerNative.getDefault(); + if (am.getCurrentUser().id == userHandle) { + am.switchUser(UserHandle.USER_OWNER); + } if (!mUserManager.removeUser(userHandle)) { Slog.w(LOG_TAG, "Couldn't remove user " + userHandle); } @@ -3120,7 +3150,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { List<UserInfo> profiles = mUserManager.getProfiles(userHandle); for (UserInfo userInfo : profiles) { int profileId = userInfo.id; - DevicePolicyData policy = getUserData(profileId); + DevicePolicyData policy = getUserDataUnchecked(profileId); final int N = policy.mAdminList.size(); if (N > 0) { for (int i=0; i<N; i++) { @@ -4240,7 +4270,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // and return null. boolean allAdminsHaveOptions = true; for (UserInfo userInfo : profiles) { - DevicePolicyData policy = getUserData(userInfo.id); + DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i=0; i < N; i++) { final ActiveAdmin active = policy.mAdminList.get(i); @@ -4471,7 +4501,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { for (int i = 0; i < PROFILES_SIZE; ++i) { // Just loop though all admins, only device or profiles // owners can have permitted lists set. - DevicePolicyData policy = getUserData(profiles.get(i).id); + DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id); final int N = policy.mAdminList.size(); for (int j = 0; j < N; j++) { ActiveAdmin admin = policy.mAdminList.get(j); @@ -4636,7 +4666,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { for (int i = 0; i < PROFILES_SIZE; ++i) { // Just loop though all admins, only device or profiles // owners can have permitted lists set. - DevicePolicyData policy = getUserData(profiles.get(i).id); + DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id); final int N = policy.mAdminList.size(); for (int j = 0; j < N; j++) { ActiveAdmin admin = policy.mAdminList.get(j); @@ -4707,6 +4737,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public UserHandle createAndInitializeUser(ComponentName who, String name, String ownerName, ComponentName profileOwnerComponent, Bundle adminExtras) { UserHandle user = createUser(who, name); + if (user == null) { + return null; + } long id = Binder.clearCallingIdentity(); try { String profileOwnerPkg = profileOwnerComponent.getPackageName(); @@ -4866,6 +4899,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } mUserManager.setUserRestriction(key, enabled, user); + if (enabled != alreadyRestricted) { + if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) { + // Send out notifications however as some clients may want to reread the + // value which actually changed due to a restriction having been applied. + final String property = Settings.Secure.SYS_PROP_SETTING_VERSION; + long version = SystemProperties.getLong(property, 0) + 1; + SystemProperties.set(property, Long.toString(version)); + + final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED; + Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name); + mContext.getContentResolver().notifyChange(url, null, true, userHandle); + } + } } finally { restoreCallingIdentity(id); } @@ -5425,7 +5471,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return Collections.emptyList(); } - DevicePolicyData policy = getUserData(profileId); + DevicePolicyData policy = getUserDataUnchecked(profileId); ActiveAdmin admin = policy.mAdminMap.get(ownerComponent); if (admin == null || admin.crossProfileWidgetProviders == null diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 334cdf6..0705fbd 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -677,7 +677,8 @@ public final class SystemServer { mSystemServiceManager.startService("com.android.server.wifi.RttService"); - if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET)) { + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) || + mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) { mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS); } |