diff options
Diffstat (limited to 'services')
24 files changed, 1094 insertions, 612 deletions
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 7f124dc..39c2891 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -220,23 +220,13 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int ENABLED = 1; private static final int DISABLED = 0; - // Arguments to rematchNetworkAndRequests() - private enum NascentState { - // Indicates a network was just validated for the first time. If the network is found to - // be unwanted (i.e. not satisfy any NetworkRequests) it is torn down. - JUST_VALIDATED, - // Indicates a network was not validated for the first time immediately prior to this call. - NOT_JUST_VALIDATED - }; private enum ReapUnvalidatedNetworks { - // Tear down unvalidated networks that have no chance (i.e. even if validated) of becoming - // the highest scoring network satisfying a NetworkRequest. This should be passed when it's - // known that there may be unvalidated networks that could potentially be reaped, and when + // Tear down networks that have no chance (e.g. even if validated) of becoming + // the highest scoring network satisfying a NetworkRequest. This should be passed when // all networks have been rematched against all NetworkRequests. REAP, - // Don't reap unvalidated networks. This should be passed when it's known that there are - // no unvalidated networks that could potentially be reaped, and when some networks have - // not yet been rematched against all NetworkRequests. + // Don't reap networks. This should be passed when some networks have not yet been + // rematched against all NetworkRequests. DONT_REAP }; @@ -493,10 +483,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void maybeLogBroadcast(NetworkAgentInfo nai, boolean connected, int type, + private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type, boolean isDefaultNetwork) { if (DBG) { - log("Sending " + (connected ? "connected" : "disconnected") + + log("Sending " + state + " broadcast for type " + type + " " + nai.name() + " isDefaultNetwork=" + isDefaultNetwork); } @@ -520,8 +510,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // Send a broadcast if this is the first network of its type or if it's the default. final boolean isDefaultNetwork = isDefaultNetwork(nai); if (list.size() == 1 || isDefaultNetwork) { - maybeLogBroadcast(nai, true, type, isDefaultNetwork); - sendLegacyNetworkBroadcast(nai, true, type); + maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork); + sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type); } } @@ -538,17 +528,19 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } + final DetailedState state = DetailedState.DISCONNECTED; + if (wasFirstNetwork || wasDefault) { - maybeLogBroadcast(nai, false, type, wasDefault); - sendLegacyNetworkBroadcast(nai, false, type); + maybeLogBroadcast(nai, state, type, wasDefault); + sendLegacyNetworkBroadcast(nai, state, type); } if (!list.isEmpty() && wasFirstNetwork) { if (DBG) log("Other network available for type " + type + ", sending connected broadcast"); final NetworkAgentInfo replacement = list.get(0); - maybeLogBroadcast(replacement, false, type, isDefaultNetwork(replacement)); - sendLegacyNetworkBroadcast(replacement, false, type); + maybeLogBroadcast(replacement, state, type, isDefaultNetwork(replacement)); + sendLegacyNetworkBroadcast(replacement, state, type); } } @@ -560,6 +552,21 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // send out another legacy broadcast - currently only used for suspend/unsuspend + // toggle + public void update(NetworkAgentInfo nai) { + final boolean isDefault = isDefaultNetwork(nai); + final DetailedState state = nai.networkInfo.getDetailedState(); + for (int type = 0; type < mTypeLists.length; type++) { + final ArrayList<NetworkAgentInfo> list = mTypeLists[type]; + final boolean isFirst = (list != null && list.size() > 0 && nai == list.get(0)); + if (isFirst || isDefault) { + maybeLogBroadcast(nai, state, type, isDefault); + sendLegacyNetworkBroadcast(nai, state, type); + } + } + } + private String naiToString(NetworkAgentInfo nai) { String name = (nai != null) ? nai.name() : "null"; String state = (nai.networkInfo != null) ? @@ -873,7 +880,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Network network = null; String subscriberId = null; - NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId); + NetworkAgentInfo nai = getDefaultNetwork(); final Network[] networks = getVpnUnderlyingNetworks(uid); if (networks != null) { @@ -1778,7 +1785,7 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(); pw.println(); - NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId); + final NetworkAgentInfo defaultNai = getDefaultNetwork(); pw.print("Active default network: "); if (defaultNai == null) { pw.println("none"); @@ -1903,8 +1910,7 @@ public class ConnectivityService extends IConnectivityManager.Stub networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { Slog.wtf(TAG, "BUG: " + nai + " has stateful capability."); } - updateCapabilities(nai, networkCapabilities, - NascentState.NOT_JUST_VALIDATED); + updateCapabilities(nai, networkCapabilities); } break; } @@ -1994,11 +2000,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log(nai.name() + " validation " + (valid ? " passed" : "failed")); if (valid != nai.lastValidated) { final int oldScore = nai.getCurrentScore(); - final NascentState nascent = (valid && !nai.everValidated) ? - NascentState.JUST_VALIDATED : NascentState.NOT_JUST_VALIDATED; nai.lastValidated = valid; nai.everValidated |= valid; - updateCapabilities(nai, nai.networkCapabilities, nascent); + updateCapabilities(nai, nai.networkCapabilities); // If score has changed, rebroadcast to NetworkFactories. b/17726566 if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); } @@ -2029,8 +2033,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nai != null && (visible != nai.lastCaptivePortalDetected)) { nai.lastCaptivePortalDetected = visible; nai.everCaptivePortalDetected |= visible; - updateCapabilities(nai, nai.networkCapabilities, - NascentState.NOT_JUST_VALIDATED); + updateCapabilities(nai, nai.networkCapabilities); } if (!visible) { setProvNotificationVisibleIntent(false, netId, null, 0, null, null, false); @@ -2049,17 +2052,22 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void linger(NetworkAgentInfo nai) { + nai.lingering = true; + nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER); + notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING); + } + // Cancel any lingering so the linger timeout doesn't teardown a network. // This should be called when a network begins satisfying a NetworkRequest. // Note: depending on what state the NetworkMonitor is in (e.g., // if it's awaiting captive portal login, or if validation failed), this // 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(); + if (!nai.lingering) return; + nai.lingering = false; + if (VDBG) log("Canceling linger of " + nai.name()); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); } @@ -2130,33 +2138,13 @@ public class ConnectivityService extends IConnectivityManager.Stub // available until we've told netd to delete it below. mNetworkForNetId.remove(nai.network.netId); } - // Since we've lost the network, go through all the requests that - // it was satisfying and see if any other factory can satisfy them. - // TODO: This logic may be better replaced with a call to rematchAllNetworksAndRequests - final ArrayList<NetworkAgentInfo> toActivate = new ArrayList<NetworkAgentInfo>(); + // Remove all previously satisfied requests. for (int i = 0; i < nai.networkRequests.size(); i++) { NetworkRequest request = nai.networkRequests.valueAt(i); NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId); if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { - if (DBG) { - log("Checking for replacement network to handle request " + request ); - } mNetworkForRequestId.remove(request.requestId); sendUpdatedScoreToFactories(request, 0); - NetworkAgentInfo alternative = null; - for (NetworkAgentInfo existing : mNetworkAgentInfos.values()) { - if (existing.satisfies(request) && - (alternative == null || - alternative.getCurrentScore() < existing.getCurrentScore())) { - alternative = existing; - } - } - if (alternative != null) { - if (DBG) log(" found replacement in " + alternative.name()); - if (!toActivate.contains(alternative)) { - toActivate.add(alternative); - } - } } } if (nai.networkRequests.get(mDefaultRequest.requestId) != null) { @@ -2165,11 +2153,7 @@ public class ConnectivityService extends IConnectivityManager.Stub requestNetworkTransitionWakelock(nai.name()); } mLegacyTypeTracker.remove(nai, wasDefault); - for (NetworkAgentInfo networkToActivate : toActivate) { - unlinger(networkToActivate); - rematchNetworkAndRequests(networkToActivate, NascentState.NOT_JUST_VALIDATED, - ReapUnvalidatedNetworks.DONT_REAP); - } + rematchAllNetworksAndRequests(null, 0); if (nai.created) { // Tell netd to clean up the configuration for this network // (routing rules, DNS, etc). @@ -2223,49 +2207,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleRegisterNetworkRequest(NetworkRequestInfo nri) { mNetworkRequests.put(nri.request, nri); - - // TODO: This logic may be better replaced with a call to rematchNetworkAndRequests - - // Check for the best currently alive network that satisfies this request - NetworkAgentInfo bestNetwork = null; - for (NetworkAgentInfo network : mNetworkAgentInfos.values()) { - if (DBG) log("handleRegisterNetworkRequest checking " + network.name()); - if (network.satisfies(nri.request)) { - if (DBG) log("apparently satisfied. currentScore=" + network.getCurrentScore()); - if (!nri.isRequest) { - // Not setting bestNetwork here as a listening NetworkRequest may be - // satisfied by multiple Networks. Instead the request is added to - // each satisfying Network and notified about each. - if (!network.addRequest(nri.request)) { - Slog.wtf(TAG, "BUG: " + network.name() + " already has " + nri.request); - } - notifyNetworkCallback(network, nri); - } else if (bestNetwork == null || - bestNetwork.getCurrentScore() < network.getCurrentScore()) { - bestNetwork = network; - } - } - } - if (bestNetwork != null) { - if (DBG) log("using " + bestNetwork.name()); - unlinger(bestNetwork); - if (!bestNetwork.addRequest(nri.request)) { - Slog.wtf(TAG, "BUG: " + bestNetwork.name() + " already has " + nri.request); - } - mNetworkForRequestId.put(nri.request.requestId, bestNetwork); - notifyNetworkCallback(bestNetwork, nri); - if (nri.request.legacyType != TYPE_NONE) { - mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork); - } - } - - if (nri.isRequest) { - if (DBG) log("sending new NetworkRequest to factories"); - final int score = bestNetwork == null ? 0 : bestNetwork.getCurrentScore(); - for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, - 0, nri.request); - } + rematchAllNetworksAndRequests(null, 0); + if (nri.isRequest && mNetworkForRequestId.get(nri.request.requestId) == null) { + sendUpdatedScoreToFactories(nri.request, 0); } } @@ -2278,43 +2222,28 @@ 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. + // This is whether it is satisfying 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; - } + if (!nai.created || nai.isVPN() || nai.lingering) return false; + 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())) { + return false; } } - return unneeded; + return true; } private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { @@ -2424,7 +2353,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (accept != nai.networkMisc.acceptUnvalidated) { int oldScore = nai.getCurrentScore(); nai.networkMisc.acceptUnvalidated = accept; - rematchAllNetworksAndRequests(nai, oldScore, NascentState.NOT_JUST_VALIDATED); + rematchAllNetworksAndRequests(nai, oldScore); sendUpdatedScoreToFactories(nai); } @@ -2733,16 +2662,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - public void captivePortalAppResponse(Network network, int response, String actionToken) { - if (response == ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS) { - enforceConnectivityInternalPermission(); - } - final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); - if (nai == null) return; - nai.networkMonitor.sendMessage(NetworkMonitor.CMD_CAPTIVE_PORTAL_APP_FINISHED, response, 0, - actionToken); - } - private ProxyInfo getDefaultProxy() { // this information is already available as a world read/writable jvm property // so this API change wouldn't have a benifit. It also breaks the passing @@ -3112,11 +3031,11 @@ public class ConnectivityService extends IConnectivityManager.Stub * are checked in Vpn class. */ @Override - public LegacyVpnInfo getLegacyVpnInfo() { + public LegacyVpnInfo getLegacyVpnInfo(int userId) { + enforceCrossUserPermission(userId); throwIfLockdownEnabled(); - int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized(mVpns) { - return mVpns.get(user).getLegacyVpnInfo(); + return mVpns.get(userId).getLegacyVpnInfo(); } } @@ -4033,7 +3952,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } catch (Exception e) { loge("Exception in setDnsServersForNetwork: " + e); } - NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId); + final NetworkAgentInfo defaultNai = getDefaultNetwork(); if (defaultNai != null && defaultNai.network.netId == netId) { setDefaultDnsSystemProperties(dnses); } @@ -4068,31 +3987,29 @@ public class ConnectivityService extends IConnectivityManager.Stub * augmented with any stateful capabilities implied from {@code networkAgent} * (e.g., validated status and captive portal status). * - * @param networkAgent the network having its capabilities updated. + * @param nai the network having its capabilities updated. * @param networkCapabilities the new network capabilities. - * @param nascent indicates whether {@code networkAgent} was validated - * (i.e. had everValidated set for the first time) immediately prior to this call. */ - private void updateCapabilities(NetworkAgentInfo networkAgent, - NetworkCapabilities networkCapabilities, NascentState nascent) { + private void updateCapabilities(NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) { // Don't modify caller's NetworkCapabilities. networkCapabilities = new NetworkCapabilities(networkCapabilities); - if (networkAgent.lastValidated) { + if (nai.lastValidated) { networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED); } else { networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED); } - if (networkAgent.lastCaptivePortalDetected) { + if (nai.lastCaptivePortalDetected) { networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL); } else { networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL); } - if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) { - synchronized (networkAgent) { - networkAgent.networkCapabilities = networkCapabilities; + if (!Objects.equals(nai.networkCapabilities, networkCapabilities)) { + final int oldScore = nai.getCurrentScore(); + synchronized (nai) { + nai.networkCapabilities = networkCapabilities; } - rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore(), nascent); - notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED); + rematchAllNetworksAndRequests(nai, oldScore); + notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } } @@ -4234,7 +4151,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // one or more NetworkRequests, or if it is a VPN. // // - Tears down newNetwork if it just became validated - // (i.e. nascent==JUST_VALIDATED) but turns out to be unneeded. + // but turns out to be unneeded. // // - If reapUnvalidatedNetworks==REAP, tears down unvalidated // networks that have no chance (i.e. even if validated) @@ -4247,17 +4164,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // as it performs better by a factor of the number of Networks. // // @param newNetwork is the network to be matched against NetworkRequests. - // @param nascent indicates if newNetwork just became validated, in which case it should be - // torn down if unneeded. // @param reapUnvalidatedNetworks indicates if an additional pass over all networks should be // performed to tear down unvalidated networks that have no chance (i.e. even if // validated) of becoming the highest scoring network. - private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, NascentState nascent, + private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, ReapUnvalidatedNetworks reapUnvalidatedNetworks) { if (!newNetwork.created) return; - if (nascent == NascentState.JUST_VALIDATED && !newNetwork.everValidated) { - loge("ERROR: nascent network not validated."); - } boolean keep = newNetwork.isVPN(); boolean isNewDefault = false; NetworkAgentInfo oldDefaultNetwork = null; @@ -4270,7 +4182,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (NetworkRequestInfo nri : mNetworkRequests.values()) { NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); if (newNetwork == currentNetwork) { - if (DBG) { + if (VDBG) { log("Network " + newNetwork.name() + " was already satisfying" + " request " + nri.request.requestId + ". No change."); } @@ -4327,10 +4239,17 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Linger any networks that are no longer needed. for (NetworkAgentInfo nai : affectedNetworks) { - if (nai.everValidated && unneeded(nai)) { - nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER); - notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING); + if (nai.lingering) { + // Already lingered. Nothing to do. This can only happen if "nai" is in + // "affectedNetworks" twice. The reasoning being that to get added to + // "affectedNetworks", "nai" must have been satisfying a NetworkRequest + // (i.e. not lingered) so it could have only been lingered by this loop. + // unneeded(nai) will be false and we'll call unlinger() below which would + // be bad, so handle it here. + } else if (unneeded(nai)) { + linger(nai); } else { + // Clear nai.networkLingered we might have added above. unlinger(nai); } } @@ -4364,7 +4283,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(), oldDefaultNetwork, true); } - mDefaultInetConditionPublished = newNetwork.everValidated ? 100 : 0; + mDefaultInetConditionPublished = newNetwork.lastValidated ? 100 : 0; mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork); notifyLockdownVpn(newNetwork); } @@ -4415,19 +4334,10 @@ public class ConnectivityService extends IConnectivityManager.Stub if (newNetwork.isVPN()) { mLegacyTypeTracker.add(TYPE_VPN, newNetwork); } - } else if (nascent == NascentState.JUST_VALIDATED) { - // Only tear down newly validated networks here. Leave unvalidated to either become - // validated (and get evaluated against peers, one losing here), or get reaped (see - // reapUnvalidatedNetworks) if they have no chance of becoming the highest scoring - // network. Networks that have been up for a while and are validated should be torn - // down via the lingering process so communication on that network is given time to - // wrap up. - if (DBG) log("Validated network turns out to be unwanted. Tear it down."); - teardownUnneededNetwork(newNetwork); } if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) { for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { - if (!nai.everValidated && unneeded(nai)) { + if (unneeded(nai)) { if (DBG) log("Reaping " + nai.name()); teardownUnneededNetwork(nai); } @@ -4446,10 +4356,8 @@ public class ConnectivityService extends IConnectivityManager.Stub * this argument, otherwise pass {@code changed.getCurrentScore()} or 0 if * {@code changed} is {@code null}. This is because NetworkCapabilities influence a * network's score. - * @param nascent indicates if {@code changed} has just been validated. */ - private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore, - NascentState nascent) { + private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore) { // TODO: This may get slow. The "changed" parameter is provided for future optimization // to avoid the slowness. It is not simply enough to process just "changed", for // example in the case where "changed"'s score decreases and another network should begin @@ -4458,17 +4366,21 @@ public class ConnectivityService extends IConnectivityManager.Stub // Optimization: Only reprocess "changed" if its score improved. This is safe because it // can only add more NetworkRequests satisfied by "changed", and this is exactly what // rematchNetworkAndRequests() handles. - if (changed != null && - (oldScore < changed.getCurrentScore() || nascent == NascentState.JUST_VALIDATED)) { - rematchNetworkAndRequests(changed, nascent, ReapUnvalidatedNetworks.REAP); + if (changed != null && oldScore < changed.getCurrentScore()) { + rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP); } else { - for (Iterator i = mNetworkAgentInfos.values().iterator(); i.hasNext(); ) { - rematchNetworkAndRequests((NetworkAgentInfo)i.next(), - NascentState.NOT_JUST_VALIDATED, + final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray( + new NetworkAgentInfo[mNetworkAgentInfos.size()]); + // Rematch higher scoring networks first to prevent requests first matching a lower + // scoring network and then a higher scoring network, which could produce multiple + // callbacks and inadvertently unlinger networks. + Arrays.sort(nais); + for (NetworkAgentInfo nai : nais) { + rematchNetworkAndRequests(nai, // Only reap the last time through the loop. Reaping before all rematching // is complete could incorrectly teardown a network that hasn't yet been // rematched. - i.hasNext() ? ReapUnvalidatedNetworks.DONT_REAP + (nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP : ReapUnvalidatedNetworks.REAP); } } @@ -4502,6 +4414,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) { NetworkInfo.State state = newInfo.getState(); NetworkInfo oldInfo = null; + final int oldScore = networkAgent.getCurrentScore(); synchronized (networkAgent) { oldInfo = networkAgent.networkInfo; networkAgent.networkInfo = newInfo; @@ -4555,13 +4468,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Consider network even though it is not yet validated. - rematchNetworkAndRequests(networkAgent, NascentState.NOT_JUST_VALIDATED, - ReapUnvalidatedNetworks.REAP); + rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP); // This has to happen after matching the requests, because callbacks are just requests. notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK); - } else if (state == NetworkInfo.State.DISCONNECTED || - state == NetworkInfo.State.SUSPENDED) { + } else if (state == NetworkInfo.State.DISCONNECTED) { networkAgent.asyncChannel.disconnect(); if (networkAgent.isVPN()) { synchronized (mProxyLock) { @@ -4573,6 +4484,16 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } + } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) || + state == NetworkInfo.State.SUSPENDED) { + // going into or coming out of SUSPEND: rescore and notify + if (networkAgent.getCurrentScore() != oldScore) { + rematchAllNetworksAndRequests(networkAgent, oldScore); + } + notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ? + ConnectivityManager.CALLBACK_SUSPENDED : + ConnectivityManager.CALLBACK_RESUMED)); + mLegacyTypeTracker.update(networkAgent); } } @@ -4587,7 +4508,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final int oldScore = nai.getCurrentScore(); nai.setCurrentScore(score); - rematchAllNetworksAndRequests(nai, oldScore, NascentState.NOT_JUST_VALIDATED); + rematchAllNetworksAndRequests(nai, oldScore); sendUpdatedScoreToFactories(nai); } @@ -4608,7 +4529,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) { + private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) { // The NetworkInfo we actually send out has no bearing on the real // state of affairs. For example, if the default connection is mobile, // and a request for HIPRI has just gone away, we need to pretend that @@ -4617,11 +4538,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // and is still connected. NetworkInfo info = new NetworkInfo(nai.networkInfo); info.setType(type); - if (connected) { - info.setDetailedState(DetailedState.CONNECTED, null, info.getExtraInfo()); + if (state != DetailedState.DISCONNECTED) { + info.setDetailedState(state, null, info.getExtraInfo()); sendConnectedBroadcast(info); } else { - info.setDetailedState(DetailedState.DISCONNECTED, info.getReason(), info.getExtraInfo()); + info.setDetailedState(state, info.getReason(), info.getExtraInfo()); Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); @@ -4637,7 +4558,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } NetworkAgentInfo newDefaultAgent = null; if (nai.networkRequests.get(mDefaultRequest.requestId) != null) { - newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId); + newDefaultAgent = getDefaultNetwork(); if (newDefaultAgent != null) { intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, newDefaultAgent.networkInfo); diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 25bd787..da552dd 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -59,6 +59,7 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -128,6 +129,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; @@ -692,6 +694,15 @@ class MountService extends IMountService.Stub } private void waitForLatch(CountDownLatch latch, String condition) { + try { + waitForLatch(latch, condition, -1); + } catch (TimeoutException ignored) { + } + } + + private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis) + throws TimeoutException { + final long startMillis = SystemClock.elapsedRealtime(); while (true) { try { if (latch.await(5000, TimeUnit.MILLISECONDS)) { @@ -703,6 +714,10 @@ class MountService extends IMountService.Stub } catch (InterruptedException e) { Slog.w(TAG, "Interrupt while waiting for " + condition); } + if (timeoutMillis > 0 && SystemClock.elapsedRealtime() > startMillis + timeoutMillis) { + throw new TimeoutException("Thread " + Thread.currentThread().getName() + + " gave up waiting for " + condition + " after " + timeoutMillis + "ms"); + } } } @@ -1645,10 +1660,12 @@ class MountService extends IMountService.Stub final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); try { mConnector.execute("volume", "partition", diskId, "public"); + waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); + } catch (TimeoutException e) { + throw new IllegalStateException(e); } - waitForLatch(latch, "partitionPublic"); } @Override @@ -1660,10 +1677,12 @@ class MountService extends IMountService.Stub final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); try { mConnector.execute("volume", "partition", diskId, "private"); + waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); + } catch (TimeoutException e) { + throw new IllegalStateException(e); } - waitForLatch(latch, "partitionPrivate"); } @Override @@ -1675,10 +1694,12 @@ class MountService extends IMountService.Stub final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); try { mConnector.execute("volume", "partition", diskId, "mixed", ratio); + waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); + } catch (TimeoutException e) { + throw new IllegalStateException(e); } - waitForLatch(latch, "partitionMixed"); } @Override diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 4c9d7d3..ad5406c 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -90,6 +90,10 @@ public class SystemConfig { // These are the app package names that should not allow IME switching. final ArraySet<String> mFixedImeApps = new ArraySet<>(); + // These are the package names of apps which should be in the 'always' + // URL-handling state upon factory reset. + final ArraySet<String> mLinkedApps = new ArraySet<>(); + public static SystemConfig getInstance() { synchronized (SystemConfig.class) { if (sInstance == null) { @@ -127,6 +131,10 @@ public class SystemConfig { return mFixedImeApps; } + public ArraySet<String> getLinkedApps() { + return mLinkedApps; + } + SystemConfig() { // Read configuration from system readPermissions(Environment.buildPath( @@ -343,6 +351,16 @@ public class SystemConfig { XmlUtils.skipCurrentTag(parser); continue; + } else if ("app-link".equals(name)) { + String pkgname = parser.getAttributeValue(null, "package"); + if (pkgname == null) { + Slog.w(TAG, "<app-link> without package in " + permFile + " at " + + parser.getPositionDescription()); + } else { + mLinkedApps.add(pkgname); + } + XmlUtils.skipCurrentTag(parser); + } else { XmlUtils.skipCurrentTag(parser); continue; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4217c59..6e94647 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -498,13 +498,11 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void run() { Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity); - synchronized (ActivityManagerService.this) { - synchronized (this) { - haveResult = true; - notifyAll(); - } - pendingAssistExtrasTimedOutLocked(this); + synchronized (this) { + haveResult = true; + notifyAll(); } + pendingAssistExtrasTimedOut(this); } } @@ -1353,6 +1351,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int FOREGROUND_PROFILE_CHANGED_MSG = 53; static final int DISPATCH_UIDS_CHANGED_MSG = 54; static final int REPORT_TIME_TRACKER_MSG = 55; + static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2014,6 +2013,9 @@ public final class ActivityManagerService extends ActivityManagerNative AppTimeTracker tracker = (AppTimeTracker)msg.obj; tracker.deliverResult(mContext); } break; + case REPORT_USER_SWITCH_COMPLETE_MSG: { + dispatchUserSwitchComplete(msg.arg1); + } break; } } }; @@ -10749,9 +10751,13 @@ public final class ActivityManagerService extends ActivityManagerNative } } - void pendingAssistExtrasTimedOutLocked(PendingAssistExtras pae) { - mPendingAssistExtras.remove(pae); - if (pae.receiver != null) { + void pendingAssistExtrasTimedOut(PendingAssistExtras pae) { + IResultReceiver receiver; + synchronized (this) { + mPendingAssistExtras.remove(pae); + receiver = pae.receiver; + } + if (receiver != null) { // Caller wants result sent back to them. try { pae.receiver.send(0, null); @@ -19910,7 +19916,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - completeSwitchAndInitalize(uss, newUserId, true, false); + completeSwitchAndInitialize(uss, newUserId, true, false); } void moveUserToForeground(UserState uss, int oldUserId, int newUserId) { @@ -19926,10 +19932,10 @@ public final class ActivityManagerService extends ActivityManagerNative } void continueUserSwitch(UserState uss, int oldUserId, int newUserId) { - completeSwitchAndInitalize(uss, newUserId, false, true); + completeSwitchAndInitialize(uss, newUserId, false, true); } - void completeSwitchAndInitalize(UserState uss, int newUserId, + void completeSwitchAndInitialize(UserState uss, int newUserId, boolean clearInitializing, boolean clearSwitching) { boolean unfrozen = false; synchronized (this) { @@ -19946,18 +19952,25 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (unfrozen) { - final int N = mUserSwitchObservers.beginBroadcast(); - for (int i=0; i<N; i++) { - try { - mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId); - } catch (RemoteException e) { - } - } - mUserSwitchObservers.finishBroadcast(); + mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); + mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, + newUserId, 0)); } stopGuestUserIfBackground(); } + /** Called on handler thread */ + void dispatchUserSwitchComplete(int userId) { + final int observerCount = mUserSwitchObservers.beginBroadcast(); + for (int i = 0; i < observerCount; i++) { + try { + mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId); + } catch (RemoteException e) { + } + } + mUserSwitchObservers.finishBroadcast(); + } + /** * Stops the guest user if it has gone to the background. */ diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 31cdcd5..8c3a950 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2963,7 +2963,6 @@ final class ActivityStack { r.state = ActivityState.FINISHING; if (mode == FINISH_IMMEDIATELY - || (mode == FINISH_AFTER_PAUSE && prevState == ActivityState.PAUSED) || prevState == ActivityState.STOPPED || prevState == ActivityState.INITIALIZING) { // If this activity is already stopped, we can just finish diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 51c6628..8a79430 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -16,6 +16,8 @@ package com.android.server.connectivity; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; + import android.content.Context; import android.net.LinkProperties; import android.net.Network; @@ -31,14 +33,71 @@ import com.android.internal.util.AsyncChannel; import com.android.server.connectivity.NetworkMonitor; import java.util.ArrayList; +import java.util.Comparator; /** * A bag class used by ConnectivityService for holding a collection of most recent * information published by a particular NetworkAgent as well as the * AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests - * interested in using it. + * interested in using it. Default sort order is descending by score. */ -public class NetworkAgentInfo { +// States of a network: +// -------------------- +// 1. registered, uncreated, disconnected, unvalidated +// This state is entered when a NetworkFactory registers a NetworkAgent in any state except +// the CONNECTED state. +// 2. registered, uncreated, connected, unvalidated +// This state is entered when a registered NetworkAgent transitions to the CONNECTED state +// ConnectivityService will tell netd to create the network and immediately transition to +// state #3. +// 3. registered, created, connected, unvalidated +// If this network can satsify the default NetworkRequest, then NetworkMonitor will +// probe for Internet connectivity. +// If this network cannot satisfy the default NetworkRequest, it will immediately be +// transitioned to state #4. +// A network may remain in this state if NetworkMonitor fails to find Internet connectivity, +// for example: +// a. a captive portal is present, or +// b. a WiFi router whose Internet backhaul is down, or +// c. a wireless connection stops transfering packets temporarily (e.g. device is in elevator +// or tunnel) but does not disconnect from the AP/cell tower, or +// d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes. +// 4. registered, created, connected, validated +// +// The device's default network connection: +// ---------------------------------------- +// Networks in states #3 and #4 may be used as a device's default network connection if they +// satisfy the default NetworkRequest. +// A network, that satisfies the default NetworkRequest, in state #4 should always be chosen +// in favor of a network, that satisfies the default NetworkRequest, in state #3. +// When deciding between two networks, that both satisfy the default NetworkRequest, to select +// for the default network connection, the one with the higher score should be chosen. +// +// When a network disconnects: +// --------------------------- +// If a network's transport disappears, for example: +// a. WiFi turned off, or +// b. cellular data turned off, or +// c. airplane mode is turned on, or +// d. a wireless connection disconnects from AP/cell tower entirely (e.g. device is out of range +// of AP for an extended period of time, or switches to another AP without roaming) +// then that network can transition from any state (#1-#4) to unregistered. This happens by +// the transport disconnecting their NetworkAgent's AsyncChannel with ConnectivityManager. +// ConnectivityService also tells netd to destroy the network. +// +// When ConnectivityService disconnects a network: +// ----------------------------------------------- +// If a network has no chance of satisfying any requests (even if it were to become validated +// and enter state #4), ConnectivityService will disconnect the NetworkAgent's AsyncChannel. +// If the network ever for any period of time had satisfied a NetworkRequest (i.e. had been +// the highest scoring that satisfied the NetworkRequest's constraints), but is no longer the +// highest scoring network for any NetworkRequest, then there will be a 30s pause before +// ConnectivityService disconnects the NetworkAgent's AsyncChannel. During this pause the +// network is considered "lingering". This pause exists to allow network communication to be +// wrapped up rather than abruptly terminated. During this pause if the network begins satisfying +// a NetworkRequest, ConnectivityService will cancel the future disconnection of the NetworkAgent's +// AsyncChannel, and the network is no longer considered "lingering". +public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public NetworkInfo networkInfo; // This Network object should always be used if possible, so as to encourage reuse of the // enclosed socket factory and connection pool. Avoid creating other Network objects. @@ -72,6 +131,13 @@ public class NetworkAgentInfo { // Whether a captive portal was found during the last network validation attempt. public boolean lastCaptivePortalDetected; + // Indicates whether the network is lingering. Networks are lingered when they become unneeded + // as a result of their NetworkRequests being satisfied by a different network, so as to allow + // communication to wrap up before the network is taken down. This usually only happens to the + // default network. Lingering ends with either the linger timeout expiring and the network + // being taken down, or the network satisfying a request again. + public boolean lingering; + // This represents the last score received from the NetworkAgent. private int currentScore; // Penalty applied to scores of Networks that have not been validated. @@ -148,7 +214,12 @@ public class NetworkAgentInfo { } int score = currentScore; - if (!everValidated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY; + // Use NET_CAPABILITY_VALIDATED here instead of lastValidated, this allows + // ConnectivityService.updateCapabilities() to compute the old score prior to updating + // networkCapabilities (with a potentially different validated state). + if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED) && !pretendValidated) { + score -= UNVALIDATED_SCORE_PENALTY; + } if (score < 0) score = 0; return score; } @@ -175,7 +246,7 @@ public class NetworkAgentInfo { linkProperties + "} nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " + "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " + - "created{" + created + "} " + + "created{" + created + "} lingering{" + lingering + "} " + "explicitlySelected{" + networkMisc.explicitlySelected + "} " + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " + @@ -188,4 +259,10 @@ public class NetworkAgentInfo { networkInfo.getSubtypeName() + ") - " + (network == null ? "null" : network.toString()) + "]"; } + + // Enables sorting in descending order of score. + @Override + public int compareTo(NetworkAgentInfo other) { + return other.getCurrentScore() - getCurrentScore(); + } } diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index e472928..2633939 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -16,6 +16,10 @@ package com.android.server.connectivity; +import static android.net.CaptivePortal.APP_RETURN_DISMISSED; +import static android.net.CaptivePortal.APP_RETURN_UNWANTED; +import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS; + import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -23,7 +27,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.CaptivePortal; import android.net.ConnectivityManager; +import android.net.ICaptivePortal; import android.net.NetworkRequest; import android.net.ProxyInfo; import android.net.TrafficStats; @@ -160,12 +166,12 @@ public class NetworkMonitor extends StateMachine { /** * Message to self indicating captive portal app finished. - * arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_DISMISSED, - * CAPTIVE_PORTAL_APP_RETURN_UNWANTED, - * CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS + * arg1 = one of: APP_RETURN_DISMISSED, + * APP_RETURN_UNWANTED, + * APP_RETURN_WANTED_AS_IS * obj = mCaptivePortalLoggedInResponseToken as String */ - public static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9; + private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9; /** * Request ConnectivityService display provisioning notification. @@ -234,7 +240,6 @@ public class NetworkMonitor extends StateMachine { private final State mLingeringState = new LingeringState(); private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null; - private String mCaptivePortalLoggedInResponseToken = null; private final LocalLog validationLogs = new LocalLog(20); // 20 lines @@ -268,8 +273,6 @@ public class NetworkMonitor extends StateMachine { mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1; - mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong()); - start(); } @@ -314,22 +317,18 @@ public class NetworkMonitor extends StateMachine { transitionTo(mEvaluatingState); return HANDLED; case CMD_CAPTIVE_PORTAL_APP_FINISHED: - if (!mCaptivePortalLoggedInResponseToken.equals((String)message.obj)) - return HANDLED; log("CaptivePortal App responded with " + message.arg1); - // Previous token was sent out, come up with a new one. - mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong()); switch (message.arg1) { - case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_DISMISSED: + case APP_RETURN_DISMISSED: sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */, 0); break; - case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS: + case APP_RETURN_WANTED_AS_IS: mDontDisplaySigninNotification = true; // TODO: Distinguish this from a network that actually validates. // Displaying the "!" on the system UI icon may still be a good idea. transitionTo(mValidatedState); break; - case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_UNWANTED: + case APP_RETURN_UNWANTED: mDontDisplaySigninNotification = true; mUserDoesNotWant = true; mConnectivityServiceHandler.sendMessage(obtainMessage( @@ -380,8 +379,18 @@ public class NetworkMonitor extends StateMachine { final Intent intent = new Intent( ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mNetworkAgentInfo.network); - intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN, - mCaptivePortalLoggedInResponseToken); + intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, + new CaptivePortal(new ICaptivePortal.Stub() { + @Override + public void appResponse(int response) { + if (response == APP_RETURN_WANTED_AS_IS) { + mContext.enforceCallingPermission( + android.Manifest.permission.CONNECTIVITY_INTERNAL, + "CaptivePortal"); + } + sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response); + } + })); intent.setFlags( Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(intent, UserHandle.CURRENT); @@ -576,9 +585,12 @@ public class NetworkMonitor extends StateMachine { switch (message.what) { case CMD_NETWORK_CONNECTED: log("Unlingered"); - // Go straight to active as we've already evaluated. - transitionTo(mValidatedState); - return HANDLED; + // If already validated, go straight to validated state. + if (mNetworkAgentInfo.lastValidated) { + transitionTo(mValidatedState); + return HANDLED; + } + return NOT_HANDLED; case CMD_LINGER_EXPIRED: if (message.arg1 != mLingerToken) return HANDLED; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 493471b..3c35f5e 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -33,6 +33,7 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import java.io.UnsupportedEncodingException; import java.util.List; +import java.util.Locale; /** * Represent a logical device of type Playback residing in Android system. @@ -317,6 +318,13 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { try { String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII"); + Locale currentLocale = mService.getContext().getResources().getConfiguration().locale; + if (currentLocale.getISO3Language().equals(iso3Language)) { + // Do not switch language if the new language is the same as the current one. + // This helps avoid accidental country variant switching from en_US to en_AU + // due to the limitation of CEC. See the warning below. + return true; + } // Don't use Locale.getAvailableLocales() since it returns a locale // which is not available on Settings. diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index 8e3334f..7a74729 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -262,7 +262,6 @@ final class DefaultPermissionGrantPolicy { && doesPackageSupportRuntimePermissions(setupPackage)) { grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId); grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(setupPackage, SETTINGS_PERMISSIONS, userId); } // Camera diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 40e7411..6da2055 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -477,6 +477,13 @@ public class PackageManagerService extends IPackageManager.Stub { final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays = new ArrayMap<String, ArrayMap<String, PackageParser.Package>>(); + /** + * Tracks new system packages [receiving in an OTA] that we expect to + * find updated user-installed versions. Keys are package name, values + * are package location. + */ + final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>(); + final Settings mSettings; boolean mRestoredSettings; @@ -709,7 +716,8 @@ public class PackageManagerService extends IPackageManager.Stub { return; } if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "Updating IntentFilterVerificationInfo for verificationId:" + verificationId); + "Updating IntentFilterVerificationInfo for package " + packageName + +" verificationId:" + verificationId); synchronized (mPackages) { if (verified) { @@ -794,8 +802,6 @@ public class PackageManagerService extends IPackageManager.Stub { boolean hasHTTPorHTTPS = filter.hasDataScheme(IntentFilter.SCHEME_HTTP) || filter.hasDataScheme(IntentFilter.SCHEME_HTTPS); if (!hasHTTPorHTTPS) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "IntentFilter does not contain any HTTP or HTTPS data scheme"); return false; } return true; @@ -2054,7 +2060,6 @@ public class PackageManagerService extends IPackageManager.Stub { // Prune any system packages that no longer exist. final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>(); - final ArrayMap<String, File> expectingBetter = new ArrayMap<>(); if (!mOnlyCore) { Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { @@ -2087,7 +2092,7 @@ public class PackageManagerService extends IPackageManager.Stub { + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.mVersionCode); removePackageLI(ps, true); - expectingBetter.put(ps.name, ps.codePath); + mExpectingBetter.put(ps.name, ps.codePath); } continue; @@ -2161,10 +2166,10 @@ public class PackageManagerService extends IPackageManager.Stub { * the userdata partition actually showed up. If they never * appeared, crawl back and revive the system version. */ - for (int i = 0; i < expectingBetter.size(); i++) { - final String packageName = expectingBetter.keyAt(i); + for (int i = 0; i < mExpectingBetter.size(); i++) { + final String packageName = mExpectingBetter.keyAt(i); if (!mPackages.containsKey(packageName)) { - final File scanFile = expectingBetter.valueAt(i); + final File scanFile = mExpectingBetter.valueAt(i); logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); @@ -2199,6 +2204,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + mExpectingBetter.clear(); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. @@ -2245,6 +2251,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (!mRestoredSettings && !onlyCore) { mSettings.applyDefaultPreferredAppsLPw(this, UserHandle.USER_OWNER); applyFactoryDefaultBrowserLPw(UserHandle.USER_OWNER); + primeDomainVerificationsLPw(UserHandle.USER_OWNER); } // If this is first boot after an OTA, and a normal boot, then @@ -2259,7 +2266,6 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.mFingerprint = Build.FINGERPRINT; } - primeDomainVerificationsLPw(); checkDefaultBrowser(); // All the changes are done during package scanning. @@ -2412,54 +2418,56 @@ public class PackageManagerService extends IPackageManager.Stub { return verifierComponentName; } - private void primeDomainVerificationsLPw() { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Start priming domain verifications"); - boolean updated = false; - ArraySet<String> allHostsSet = new ArraySet<>(); - for (PackageParser.Package pkg : mPackages.values()) { - final String packageName = pkg.packageName; - if (!hasDomainURLs(pkg)) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "No priming domain verifications for " + - "package with no domain URLs: " + packageName); - continue; - } - if (!pkg.isSystemApp()) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "No priming domain verifications for a non system package : " + - packageName); - continue; - } - for (PackageParser.Activity a : pkg.activities) { - for (ActivityIntentInfo filter : a.intents) { - if (hasValidDomains(filter)) { - allHostsSet.addAll(filter.getHostsList()); + private void primeDomainVerificationsLPw(int userId) { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.d(TAG, "Priming domain verifications in user " + userId); + } + + SystemConfig systemConfig = SystemConfig.getInstance(); + ArraySet<String> packages = systemConfig.getLinkedApps(); + ArraySet<String> domains = new ArraySet<String>(); + + for (String packageName : packages) { + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg != null) { + if (!pkg.isSystemApp()) { + Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig <app-link>"); + continue; + } + + domains.clear(); + for (PackageParser.Activity a : pkg.activities) { + for (ActivityIntentInfo filter : a.intents) { + if (hasValidDomains(filter)) { + domains.addAll(filter.getHostsList()); + } } } + + if (domains.size() > 0) { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.v(TAG, " + " + packageName); + } + // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual + // state w.r.t. the formal app-linkage "no verification attempted" state; + // and then 'always' in the per-user state actually used for intent resolution. + final IntentFilterVerificationInfo ivi; + ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, + new ArrayList<String>(domains)); + ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); + mSettings.updateIntentFilterVerificationStatusLPw(packageName, + INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId); + } else { + Slog.w(TAG, "Sysconfig <app-link> package '" + packageName + + "' does not handle web links"); + } + } else { + Slog.w(TAG, "Unknown package '" + packageName + "' in sysconfig <app-link>"); } - if (allHostsSet.size() == 0) { - allHostsSet.add("*"); - } - ArrayList<String> allHostsList = new ArrayList<>(allHostsSet); - IntentFilterVerificationInfo ivi = - mSettings.createIntentFilterVerificationIfNeededLPw(packageName, allHostsList); - if (ivi != null) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "Priming domain verifications for package: " + packageName + - " with hosts:" + ivi.getDomainsString()); - ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS); - updated = true; - } - else { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "No priming domain verifications for package: " + packageName); - } - allHostsSet.clear(); - } - if (updated) { - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, - "Will need to write primed domain verifications"); } - if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "End priming domain verifications"); + + scheduleWritePackageRestrictionsLocked(userId); + scheduleWriteSettingsLocked(); } private void applyFactoryDefaultBrowserLPw(int userId) { @@ -2467,7 +2475,7 @@ public class PackageManagerService extends IPackageManager.Stub { // with a product-specific overlay used for vendor customization. String browserPkg = mContext.getResources().getString( com.android.internal.R.string.default_browser); - if (browserPkg != null) { + if (!TextUtils.isEmpty(browserPkg)) { // non-empty string => required to be a known package PackageSetting ps = mSettings.mPackages.get(browserPkg); if (ps == null) { @@ -3146,6 +3154,28 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.PERMISSION_DENIED; } + @Override + public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) { + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "isPermissionRevokedByPolicy for user " + userId); + } + + if (checkPermission(permission, packageName, userId) + == PackageManager.PERMISSION_GRANTED) { + return false; + } + + final long identity = Binder.clearCallingIdentity(); + try { + final int flags = getPermissionFlags(permission, packageName, userId); + return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + /** * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller. @@ -3428,8 +3458,12 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } - if (READ_EXTERNAL_STORAGE.equals(name) - || WRITE_EXTERNAL_STORAGE.equals(name)) { + // Only need to do this if user is initialized. Otherwise it's a new user + // and there are no processes running as the user yet and there's no need + // to make an expensive call to remount processes for the changed permissions. + if ((READ_EXTERNAL_STORAGE.equals(name) + || WRITE_EXTERNAL_STORAGE.equals(name)) + && sUserManager.isInitialized(userId)) { final long token = Binder.clearCallingIdentity(); try { final StorageManager storage = mContext.getSystemService(StorageManager.class); @@ -5635,7 +5669,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkg.mVersionCode <= ps.versionCode) { // The system package has been updated and the code path does not match // Ignore entry. Skip it. - Slog.i(TAG, "Package " + ps.name + " at " + scanFile + if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile + " ignored: updated version " + ps.versionCode + " better than this " + pkg.mVersionCode); if (!updatedPkg.codePath.equals(scanFile)) { @@ -5648,7 +5682,10 @@ public class PackageManagerService extends IPackageManager.Stub { updatedPkg.resourcePathString = scanFile.toString(); } updatedPkg.pkg = pkg; - throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, null); + throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, + "Package " + ps.name + " at " + scanFile + + " ignored: updated version " + ps.versionCode + + " better than this " + pkg.mVersionCode); } else { // The current app on the system partition is better than // what we have updated to on the data partition; switch @@ -6431,20 +6468,29 @@ public class PackageManagerService extends IPackageManager.Stub { // scanned APK is both already known and at the path previously established // for it. Previously unknown packages we pick up normally, but if we have an // a priori expectation about this package's install presence, enforce it. + // With a singular exception for new system packages. When an OTA contains + // a new system package, we allow the codepath to change from a system location + // to the user-installed location. If we don't allow this change, any newer, + // user-installed version of the application will be ignored. if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) { - PackageSetting known = mSettings.peekPackageLPr(pkg.packageName); - if (known != null) { - if (DEBUG_PACKAGE_SCANNING) { - Log.d(TAG, "Examining " + pkg.codePath - + " and requiring known paths " + known.codePathString - + " & " + known.resourcePathString); - } - if (!pkg.applicationInfo.getCodePath().equals(known.codePathString) - || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) { - throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED, - "Application package " + pkg.packageName - + " found at " + pkg.applicationInfo.getCodePath() - + " but expected at " + known.codePathString + "; ignoring."); + if (mExpectingBetter.containsKey(pkg.packageName)) { + logCriticalInfo(Log.WARN, + "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName); + } else { + PackageSetting known = mSettings.peekPackageLPr(pkg.packageName); + if (known != null) { + if (DEBUG_PACKAGE_SCANNING) { + Log.d(TAG, "Examining " + pkg.codePath + + " and requiring known paths " + known.codePathString + + " & " + known.resourcePathString); + } + if (!pkg.applicationInfo.getCodePath().equals(known.codePathString) + || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) { + throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED, + "Application package " + pkg.packageName + + " found at " + pkg.applicationInfo.getCodePath() + + " but expected at " + known.codePathString + "; ignoring."); + } } } } @@ -13694,6 +13740,7 @@ public class PackageManagerService extends IPackageManager.Stub { clearPackagePreferredActivitiesLPw(null, userId); mSettings.applyDefaultPreferredAppsLPw(this, userId); applyFactoryDefaultBrowserLPw(userId); + primeDomainVerificationsLPw(userId); scheduleWritePackageRestrictionsLocked(userId); } @@ -14808,7 +14855,7 @@ public class PackageManagerService extends IPackageManager.Stub { pw.println(); int count = mSettings.mPackages.size(); if (count == 0) { - pw.println("No domain preferred apps!"); + pw.println("No applications!"); pw.println(); } else { final String prefix = " "; @@ -14817,45 +14864,41 @@ public class PackageManagerService extends IPackageManager.Stub { pw.println("No domain preferred apps!"); pw.println(); } else { - pw.println("Domain preferred apps status:"); + pw.println("App verification status:"); pw.println(); count = 0; for (PackageSetting ps : allPackageSettings) { IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo(); if (ivi == null || ivi.getPackageName() == null) continue; - pw.println(prefix + "Package Name: " + ivi.getPackageName()); + pw.println(prefix + "Package: " + ivi.getPackageName()); pw.println(prefix + "Domains: " + ivi.getDomainsString()); - pw.println(prefix + "Status: " + ivi.getStatusString()); + pw.println(prefix + "Status: " + ivi.getStatusString()); pw.println(); count++; } if (count == 0) { - pw.println(prefix + "No domain preferred app status!"); + pw.println(prefix + "No app verification established."); pw.println(); } for (int userId : sUserManager.getUserIds()) { - pw.println("Domain preferred apps for User " + userId + ":"); + pw.println("App linkages for user " + userId + ":"); pw.println(); count = 0; for (PackageSetting ps : allPackageSettings) { - IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo(); - if (ivi == null || ivi.getPackageName() == null) { - continue; - } final int status = ps.getDomainVerificationStatusForUser(userId); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) { continue; } - pw.println(prefix + "Package Name: " + ivi.getPackageName()); - pw.println(prefix + "Domains: " + ivi.getDomainsString()); + pw.println(prefix + "Package: " + ps.name); + pw.println(prefix + "Domains: " + dumpDomainString(ps.name)); String statusStr = IntentFilterVerificationInfo. getStatusStringFromValue(status); - pw.println(prefix + "Status: " + statusStr); + pw.println(prefix + "Status: " + statusStr); pw.println(); count++; } if (count == 0) { - pw.println(prefix + "No domain preferred apps!"); + pw.println(prefix + "No configured app linkages."); pw.println(); } } @@ -14977,6 +15020,35 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private String dumpDomainString(String packageName) { + List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName); + List<IntentFilter> filters = getAllIntentFilters(packageName); + + ArraySet<String> result = new ArraySet<>(); + if (iviList.size() > 0) { + for (IntentFilterVerificationInfo ivi : iviList) { + for (String host : ivi.getDomains()) { + result.add(host); + } + } + } + if (filters != null && filters.size() > 0) { + for (IntentFilter filter : filters) { + if (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) || + filter.hasDataScheme(IntentFilter.SCHEME_HTTPS)) { + result.addAll(filter.getHostsList()); + } + } + } + + StringBuilder sb = new StringBuilder(result.size() * 16); + for (String domain : result) { + if (sb.length() > 0) sb.append(" "); + sb.append(domain); + } + return sb.toString(); + } + // ------- apps on sdcard specific code ------- static final boolean DEBUG_SD_INSTALL = false; @@ -15896,19 +15968,12 @@ public class PackageManagerService extends IPackageManager.Stub { mInstaller.createUserConfig(userHandle); mSettings.createNewUserLILPw(this, mInstaller, userHandle); applyFactoryDefaultBrowserLPw(userHandle); + primeDomainVerificationsLPw(userHandle); } } - void newUserCreatedLILPw(final int userHandle) { - // We cannot grant the default permissions with a lock held as - // we query providers from other components for default handlers - // such as enabled IMEs, etc. - mHandler.post(new Runnable() { - @Override - public void run() { - mDefaultPermissionPolicy.grantDefaultPermissions(userHandle); - } - }); + void newUserCreated(final int userHandle) { + mDefaultPermissionPolicy.grantDefaultPermissions(userHandle); } @Override diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 1a79b4e..23cb767 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1220,6 +1220,7 @@ public class UserManagerService extends IUserManager.Stub { final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0; final long ident = Binder.clearCallingIdentity(); UserInfo userInfo = null; + final int userId; try { synchronized (mInstallLock) { synchronized (mPackagesLock) { @@ -1240,7 +1241,7 @@ public class UserManagerService extends IUserManager.Stub { if (isGuest && findCurrentGuestUserLocked() != null) { return null; } - int userId = getNextAvailableIdLocked(); + userId = getNextAvailableIdLocked(); userInfo = new UserInfo(userId, name, null, flags); userInfo.serialNumber = mNextSerialNumber++; long now = System.currentTimeMillis(); @@ -1274,9 +1275,9 @@ public class UserManagerService extends IUserManager.Stub { updateUserIdsLocked(); Bundle restrictions = new Bundle(); mUserRestrictions.append(userId, restrictions); - mPm.newUserCreatedLILPw(userId); } } + mPm.newUserCreated(userId); if (userInfo != null) { Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); @@ -2015,4 +2016,12 @@ public class UserManagerService extends IUserManager.Stub { } } } + + /** + * @param userId + * @return whether the user has been initialized yet + */ + boolean isInitialized(int userId) { + return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0; + } } diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java index 5877b3e..9095f57 100644 --- a/services/core/java/com/android/server/policy/BarController.java +++ b/services/core/java/com/android/server/policy/BarController.java @@ -155,7 +155,7 @@ public class BarController { } private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) { - if (win.hasDrawnLw()) { + if (win.isDrawnLw()) { final boolean vis = win.isVisibleLw(); final boolean anim = win.isAnimatingLw(); if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) { @@ -198,7 +198,7 @@ public class BarController { } public boolean checkHiddenLw() { - if (mWin != null && mWin.hasDrawnLw()) { + if (mWin != null && mWin.isDrawnLw()) { if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) { updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 3393d7d..87efb8d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3048,14 +3048,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void launchAssistAction() { - launchAssistAction(null, Integer.MIN_VALUE); - } - - private void launchAssistAction(String hint) { - launchAssistAction(hint, Integer.MIN_VALUE); - } - private void launchAssistAction(String hint, int deviceId) { sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST); Bundle args = null; @@ -3063,8 +3055,29 @@ public class PhoneWindowManager implements WindowManagerPolicy { args = new Bundle(); args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, deviceId); } - ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) - .launchAssistAction(hint, UserHandle.myUserId(), args); + if ((mContext.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) { + // On TV, use legacy handling until assistants are implemented in the proper way. + ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) + .launchLegacyAssist(hint, UserHandle.myUserId(), args); + } else { + try { + if (hint != null) { + if (args == null) { + args = new Bundle(); + } + args.putBoolean(hint, true); + } + IStatusBarService statusbar = getStatusBarService(); + if (statusbar != null) { + statusbar.startAssist(args); + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when starting assist", e); + // re-acquire status bar service next time it is needed. + mStatusBarService = null; + } + } } private void startActivityAsUser(Intent intent, UserHandle handle) { diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index dd2286f..04b2f60 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -45,6 +45,8 @@ import android.util.SparseArray; import com.android.internal.content.PackageMonitor; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.LocalServices; +import com.android.server.statusbar.StatusBarManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -240,16 +242,24 @@ public class SearchManagerService extends ISearchManager.Stub { } @Override - public ComponentName getAssistIntent(int userHandle) { + public void launchAssist(Bundle args) { + StatusBarManagerInternal statusBarManager = + LocalServices.getService(StatusBarManagerInternal.class); + if (statusBarManager != null) { + statusBarManager.startAssist(args); + } + } + + private ComponentName getLegacyAssistComponent(int userHandle) { try { userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userHandle, true, false, "getAssistIntent", null); + Binder.getCallingUid(), userHandle, true, false, "getLegacyAssistComponent", null); IPackageManager pm = AppGlobals.getPackageManager(); Intent assistIntent = new Intent(Intent.ACTION_ASSIST); ResolveInfo info = pm.resolveIntent(assistIntent, - assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()), - PackageManager.MATCH_DEFAULT_ONLY, userHandle); + assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()), + PackageManager.MATCH_DEFAULT_ONLY, userHandle); if (info != null) { return new ComponentName( info.activityInfo.applicationInfo.packageName, @@ -257,16 +267,16 @@ public class SearchManagerService extends ISearchManager.Stub { } } catch (RemoteException re) { // Local call - Log.e(TAG, "RemoteException in getAssistIntent: " + re); + Log.e(TAG, "RemoteException in getLegacyAssistComponent: " + re); } catch (Exception e) { - Log.e(TAG, "Exception in getAssistIntent: " + e); + Log.e(TAG, "Exception in getLegacyAssistComponent: " + e); } return null; } @Override - public boolean launchAssistAction(String hint, int userHandle, Bundle args) { - ComponentName comp = getAssistIntent(userHandle); + public boolean launchLegacyAssist(String hint, int userHandle, Bundle args) { + ComponentName comp = getLegacyAssistComponent(userHandle); if (comp == null) { return false; } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 4692403..130815e 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -16,6 +16,8 @@ package com.android.server.statusbar; +import android.os.Bundle; + import com.android.server.notification.NotificationDelegate; public interface StatusBarManagerInternal { @@ -25,4 +27,5 @@ public interface StatusBarManagerInternal { void notificationLightOff(); void showScreenPinningRequest(); void showAssistDisclosure(); + void startAssist(Bundle args); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 5ceb6ad..2a817ea 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -18,6 +18,7 @@ package com.android.server.statusbar; import android.app.StatusBarManager; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -165,6 +166,16 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } } + + @Override + public void startAssist(Bundle args) { + if (mBar != null) { + try { + mBar.startAssist(args); + } catch (RemoteException e) { + } + } + } }; // ================================================================================ @@ -526,6 +537,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } + @Override + public void startAssist(Bundle args) { + if (mBar != null) { + try { + mBar.startAssist(args); + } catch (RemoteException ex) {} + } + } + private void enforceStatusBar() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR, "StatusBarManagerService"); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 11eb572..7630178 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -349,6 +349,7 @@ public class WindowAnimator { "Now policy hidden: " + win); } else { boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null + && !mPostKeyguardExitAnimation.hasEnded() && !winAnimator.mKeyguardGoingAwayAnimation && win.hasDrawnLw() && win.mAttachedWindow == null @@ -499,8 +500,7 @@ public class WindowAnimator { mPostKeyguardExitAnimation.getStartOffset(), mPostKeyguardExitAnimation.getDuration()); mKeyguardGoingAway = false; - } else if (mCurrentTime - mPostKeyguardExitAnimation.getStartTime() - > mPostKeyguardExitAnimation.getDuration()) { + } else if (mPostKeyguardExitAnimation.hasEnded()) { // Done with the animation, reset. if (DEBUG_KEYGUARD) Slog.v(TAG, "Done with Keyguard exit animations."); mPostKeyguardExitAnimation = null; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ac4fea8..ec566bc 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -10978,6 +10978,13 @@ public class WindowManagerService extends IWindowManager.Stub if (mLastDispatchedSystemUiVisibility == visibility) { return; } + final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility) + // We are only interested in differences of one of the + // clearable flags... + & View.SYSTEM_UI_CLEARABLE_FLAGS + // ...if it has actually been cleared. + & ~visibility; + mLastDispatchedSystemUiVisibility = visibility; mInputManager.setSystemUiVisibility(visibility); final WindowList windows = getDefaultWindowListLocked(); @@ -10986,12 +10993,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowState ws = windows.get(i); try { int curValue = ws.mSystemUiVisibility; - int diff = curValue ^ visibility; - // We are only interested in differences of one of the - // clearable flags... - diff &= View.SYSTEM_UI_CLEARABLE_FLAGS; - // ...if it has actually been cleared. - diff &= ~visibility; + int diff = (curValue ^ visibility) & globalDiff; int newValue = (curValue&~diff) | (visibility&diff); if (newValue != curValue) { ws.mSeq++; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b918a25..c2548de 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1104,6 +1104,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { * Returns true if the window has a surface that it has drawn a * complete UI in to. */ + @Override public boolean isDrawnLw() { return mHasSurface && !mDestroying && (mWinAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ed4fe24..5d05f32 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -60,7 +60,6 @@ import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.hardware.usb.UsbManager; import android.media.AudioManager; import android.media.IAudioService; import android.net.ConnectivityManager; @@ -5446,10 +5445,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userHandle); - } else if (UserManager.DISALLOW_USB_FILE_TRANSFER.equals(key)) { - UsbManager manager = - (UsbManager) mContext.getSystemService(Context.USB_SERVICE); - manager.setCurrentFunction("none"); } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index fb8a5bb..cb9c6a7 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -20,8 +20,24 @@ import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.getNetworkTypeName; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; +import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.mockito.Matchers.anyInt; @@ -135,6 +151,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; private final Thread mThread; + private final ConditionVariable mDisconnected = new ConditionVariable(); private int mScore; private NetworkAgent mNetworkAgent; @@ -161,7 +178,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext, "Mock" + typeName, mNetworkInfo, mNetworkCapabilities, new LinkProperties(), mScore, new NetworkMisc()) { - public void unwanted() {} + public void unwanted() { mDisconnected.open(); } }; initComplete.open(); Looper.loop(); @@ -176,8 +193,18 @@ public class ConnectivityServiceTest extends AndroidTestCase { mNetworkAgent.sendNetworkScore(mScore); } + public void addCapability(int capability) { + mNetworkCapabilities.addCapability(capability); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void connectWithoutInternet() { + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + /** - * Transition this NetworkAgent to CONNECTED state. + * Transition this NetworkAgent to CONNECTED state with NET_CAPABILITY_INTERNET. * @param validated Indicate if network should pretend to be validated. */ public void connect(boolean validated) { @@ -210,8 +237,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); } - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - mNetworkAgent.sendNetworkInfo(mNetworkInfo); + connectWithoutInternet(); if (validated) { // Wait for network to validate. @@ -231,6 +257,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { public Network getNetwork() { return new Network(mNetworkAgent.netId); } + + public ConditionVariable getDisconnectedCV() { + return mDisconnected; + } } private static class MockNetworkFactory extends NetworkFactory { @@ -555,6 +585,34 @@ public class ConnectivityServiceTest extends AndroidTestCase { } @LargeTest + public void testUnlingeringDoesNotValidate() throws Exception { + // Test bringing up unvalidated cellular. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + ConditionVariable cv = waitForConnectivityBroadcasts(1); + mCellNetworkAgent.connect(false); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_CELLULAR); + assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + cv = waitForConnectivityBroadcasts(2); + mWiFiNetworkAgent.connect(true); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + // Test WiFi disconnect. + cv = waitForConnectivityBroadcasts(2); + mWiFiNetworkAgent.disconnect(); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Unlingering a network should not cause it to be marked as validated. + assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + } + + @LargeTest public void testCellularOutscoresWeakWifi() throws Exception { // Test bringing up validated cellular. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); @@ -582,6 +640,107 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent.disconnect(); } + @LargeTest + public void testReapingNetwork() throws Exception { + // Test bringing up WiFi without NET_CAPABILITY_INTERNET. + // Expect it to be torn down immediately because it satisfies no requests. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + ConditionVariable cv = mWiFiNetworkAgent.getDisconnectedCV(); + mWiFiNetworkAgent.connectWithoutInternet(); + waitFor(cv); + // Test bringing up cellular without NET_CAPABILITY_INTERNET. + // Expect it to be torn down immediately because it satisfies no requests. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + cv = mCellNetworkAgent.getDisconnectedCV(); + mCellNetworkAgent.connectWithoutInternet(); + waitFor(cv); + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + cv = waitForConnectivityBroadcasts(1); + mWiFiNetworkAgent.connect(true); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test bringing up unvalidated cellular. + // Expect it to be torn down because it could never be the highest scoring network + // satisfying the default request even if it validated. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + cv = mCellNetworkAgent.getDisconnectedCV(); + mCellNetworkAgent.connect(false); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + cv = mWiFiNetworkAgent.getDisconnectedCV(); + mWiFiNetworkAgent.disconnect(); + waitFor(cv); + } + + @LargeTest + public void testCellularFallback() throws Exception { + // Test bringing up validated cellular. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + ConditionVariable cv = waitForConnectivityBroadcasts(1); + mCellNetworkAgent.connect(true); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + cv = waitForConnectivityBroadcasts(2); + mWiFiNetworkAgent.connect(true); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + // Reevaluate WiFi (it'll instantly fail DNS). + cv = waitForConnectivityBroadcasts(2); + assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork()); + // Should quickly fall back to Cellular. + waitFor(cv); + assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Reevaluate cellular (it'll instantly fail DNS). + cv = waitForConnectivityBroadcasts(2); + assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); + // Should quickly fall back to WiFi. + waitFor(cv); + assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + verifyActiveNetwork(TRANSPORT_WIFI); + mCellNetworkAgent.disconnect(); + mWiFiNetworkAgent.disconnect(); + } + + @LargeTest + public void testWiFiFallback() throws Exception { + // Test bringing up unvalidated WiFi. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + ConditionVariable cv = waitForConnectivityBroadcasts(1); + mWiFiNetworkAgent.connect(false); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test bringing up validated cellular. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + cv = waitForConnectivityBroadcasts(2); + mCellNetworkAgent.connect(true); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Reevaluate cellular (it'll instantly fail DNS). + cv = waitForConnectivityBroadcasts(2); + assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); + // Should quickly fall back to WiFi. + waitFor(cv); + assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + verifyActiveNetwork(TRANSPORT_WIFI); + mCellNetworkAgent.disconnect(); + mWiFiNetworkAgent.disconnect(); + } + enum CallbackState { NONE, AVAILABLE, @@ -736,10 +895,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); } - @LargeTest - public void testNetworkFactoryRequests() throws Exception { + private void tryNetworkFactoryRequests(int capability) throws Exception { NetworkCapabilities filter = new NetworkCapabilities(); - filter.addCapability(NET_CAPABILITY_INTERNET); + filter.addCapability(capability); final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); handlerThread.start(); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), @@ -747,57 +905,91 @@ public class ConnectivityServiceTest extends AndroidTestCase { testFactory.setScoreFilter(40); ConditionVariable cv = testFactory.getNetworkStartedCV(); testFactory.register(); + int expectedRequestCount = 1; + NetworkCallback networkCallback = null; + // For non-INTERNET capabilities we cannot rely on the default request being present, so + // add one. + if (capability != NET_CAPABILITY_INTERNET) { + testFactory.waitForNetworkRequests(1); + assertFalse(testFactory.getMyStartRequested()); + NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build(); + networkCallback = new NetworkCallback(); + mCm.requestNetwork(request, networkCallback); + expectedRequestCount++; + } waitFor(cv); - assertEquals(1, testFactory.getMyRequestCount()); - assertEquals(true, testFactory.getMyStartRequested()); + assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); + assertTrue(testFactory.getMyStartRequested()); - // now bring in a higher scored network + // Now bring in a higher scored network. MockNetworkAgent testAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); - cv = waitForConnectivityBroadcasts(1); - ConditionVariable cvRelease = testFactory.getNetworkStoppedCV(); - testAgent.connect(true); + // Rather than create a validated network which complicates things by registering it's + // own NetworkRequest during startup, just bump up the score to cancel out the + // unvalidated penalty. + testAgent.adjustScore(40); + cv = testFactory.getNetworkStoppedCV(); + testAgent.connect(false); + testAgent.addCapability(capability); waitFor(cv); - // part of the bringup makes another network request and then releases it - // wait for the release - waitFor(cvRelease); - assertEquals(false, testFactory.getMyStartRequested()); - testFactory.waitForNetworkRequests(1); + assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); + assertFalse(testFactory.getMyStartRequested()); - // bring in a bunch of requests.. + // Bring in a bunch of requests. ConnectivityManager.NetworkCallback[] networkCallbacks = new ConnectivityManager.NetworkCallback[10]; for (int i = 0; i< networkCallbacks.length; i++) { networkCallbacks[i] = new ConnectivityManager.NetworkCallback(); NetworkRequest.Builder builder = new NetworkRequest.Builder(); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + builder.addCapability(capability); mCm.requestNetwork(builder.build(), networkCallbacks[i]); } - testFactory.waitForNetworkRequests(11); - assertEquals(false, testFactory.getMyStartRequested()); + testFactory.waitForNetworkRequests(10 + expectedRequestCount); + assertFalse(testFactory.getMyStartRequested()); - // remove the requests + // Remove the requests. for (int i = 0; i < networkCallbacks.length; i++) { mCm.unregisterNetworkCallback(networkCallbacks[i]); } - testFactory.waitForNetworkRequests(1); - assertEquals(false, testFactory.getMyStartRequested()); + testFactory.waitForNetworkRequests(expectedRequestCount); + assertFalse(testFactory.getMyStartRequested()); - // drop the higher scored network - cv = waitForConnectivityBroadcasts(1); + // Drop the higher scored network. + cv = testFactory.getNetworkStartedCV(); testAgent.disconnect(); waitFor(cv); - assertEquals(1, testFactory.getMyRequestCount()); - assertEquals(true, testFactory.getMyStartRequested()); + assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); + assertTrue(testFactory.getMyStartRequested()); testFactory.unregister(); + if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback); handlerThread.quit(); } @LargeTest + public void testNetworkFactoryRequests() throws Exception { + tryNetworkFactoryRequests(NET_CAPABILITY_MMS); + tryNetworkFactoryRequests(NET_CAPABILITY_SUPL); + tryNetworkFactoryRequests(NET_CAPABILITY_DUN); + tryNetworkFactoryRequests(NET_CAPABILITY_FOTA); + tryNetworkFactoryRequests(NET_CAPABILITY_IMS); + tryNetworkFactoryRequests(NET_CAPABILITY_CBS); + tryNetworkFactoryRequests(NET_CAPABILITY_WIFI_P2P); + tryNetworkFactoryRequests(NET_CAPABILITY_IA); + tryNetworkFactoryRequests(NET_CAPABILITY_RCS); + tryNetworkFactoryRequests(NET_CAPABILITY_XCAP); + tryNetworkFactoryRequests(NET_CAPABILITY_EIMS); + tryNetworkFactoryRequests(NET_CAPABILITY_NOT_METERED); + tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET); + tryNetworkFactoryRequests(NET_CAPABILITY_TRUSTED); + tryNetworkFactoryRequests(NET_CAPABILITY_NOT_VPN); + // Skipping VALIDATED and CAPTIVE_PORTAL as they're disallowed. + } + + @LargeTest public void testNoMutableNetworkRequests() throws Exception { PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0); NetworkRequest.Builder builder = new NetworkRequest.Builder(); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + builder.addCapability(NET_CAPABILITY_VALIDATED); try { mCm.requestNetwork(builder.build(), new NetworkCallback()); fail(); @@ -807,7 +999,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { fail(); } catch (IllegalArgumentException expected) {} builder = new NetworkRequest.Builder(); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + builder.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL); try { mCm.requestNetwork(builder.build(), new NetworkCallback()); fail(); @@ -818,6 +1010,71 @@ public class ConnectivityServiceTest extends AndroidTestCase { } catch (IllegalArgumentException expected) {} } + @LargeTest + public void testMMSonWiFi() throws Exception { + // Test bringing up cellular without MMS NetworkRequest gets reaped + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); + ConditionVariable cv = mCellNetworkAgent.getDisconnectedCV(); + mCellNetworkAgent.connectWithoutInternet(); + waitFor(cv); + waitFor(new Criteria() { + public boolean get() { return mCm.getAllNetworks().length == 0; } }); + verifyNoNetwork(); + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + cv = waitForConnectivityBroadcasts(1); + mWiFiNetworkAgent.connect(true); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + // Register MMS NetworkRequest + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(builder.build(), networkCallback); + // Test bringing up unvalidated cellular with MMS + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); + cv = networkCallback.getConditionVariable(); + mCellNetworkAgent.connectWithoutInternet(); + waitFor(cv); + assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback()); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test releasing NetworkRequest disconnects cellular with MMS + cv = mCellNetworkAgent.getDisconnectedCV(); + mCm.unregisterNetworkCallback(networkCallback); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + } + + @LargeTest + public void testMMSonCell() throws Exception { + // Test bringing up cellular without MMS + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + ConditionVariable cv = waitForConnectivityBroadcasts(1); + mCellNetworkAgent.connect(false); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Register MMS NetworkRequest + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(builder.build(), networkCallback); + // Test bringing up MMS cellular network + cv = networkCallback.getConditionVariable(); + MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS); + mmsNetworkAgent.connectWithoutInternet(); + waitFor(cv); + assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback()); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent + cv = mmsNetworkAgent.getDisconnectedCV(); + mCm.unregisterNetworkCallback(networkCallback); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_CELLULAR); + } + // @Override // public void tearDown() throws Exception { // super.tearDown(); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index e7716c2..81b4857 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -19,12 +19,10 @@ package com.android.server.usb; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; @@ -54,7 +52,6 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -67,9 +64,25 @@ import java.util.Scanner; */ public class UsbDeviceManager { - private static final String TAG = UsbDeviceManager.class.getSimpleName(); + private static final String TAG = "UsbDeviceManager"; private static final boolean DEBUG = false; + /** + * The persistent property which stores whether adb is enabled or not. + * May also contain vendor-specific default functions for testing purposes. + */ + private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config"; + + /** + * The non-persistent property which stores the current USB settings. + */ + private static final String USB_CONFIG_PROPERTY = "sys.usb.config"; + + /** + * The non-persistent property which stores the current USB actual state. + */ + private static final String USB_STATE_PROPERTY = "sys.usb.state"; + private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; private static final String ACCESSORY_START_MATCH = @@ -92,6 +105,7 @@ public class UsbDeviceManager { private static final int MSG_BOOT_COMPLETED = 4; private static final int MSG_USER_SWITCHED = 5; private static final int MSG_SET_USB_DATA_UNLOCKED = 6; + private static final int MSG_UPDATE_USER_RESTRICTIONS = 7; private static final int AUDIO_MODE_SOURCE = 1; @@ -184,12 +198,6 @@ public class UsbDeviceManager { } } - public void setCurrentSettings(UsbSettingsManager settings) { - synchronized (mLock) { - mCurrentSettings = settings; - } - } - private UsbSettingsManager getCurrentSettings() { synchronized (mLock) { return mCurrentSettings; @@ -221,6 +229,22 @@ public class UsbDeviceManager { mHandler.sendEmptyMessage(MSG_SYSTEM_READY); } + public void bootCompleted() { + if (DEBUG) Slog.d(TAG, "boot completed"); + mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); + } + + public void setCurrentUser(int userId, UsbSettingsManager settings) { + synchronized (mLock) { + mCurrentSettings = settings; + mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget(); + } + } + + public void updateUserRestrictions() { + mHandler.sendEmptyMessage(MSG_UPDATE_USER_RESTRICTIONS); + } + private void startAccessoryMode() { if (!mHasUsbAccessory) return; @@ -270,46 +294,6 @@ public class UsbDeviceManager { } } - private static String addFunction(String functions, String function) { - if ("none".equals(functions)) { - return function; - } - if (!containsFunction(functions, function)) { - if (functions.length() > 0) { - functions += ","; - } - functions += function; - } - return functions; - } - - private static String removeFunction(String functions, String function) { - String[] split = functions.split(","); - for (int i = 0; i < split.length; i++) { - if (function.equals(split[i])) { - split[i] = null; - } - } - if (split.length == 1 && split[0] == null) { - return "none"; - } - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < split.length; i++) { - String s = split[i]; - if (s != null) { - if (builder.length() > 0) { - builder.append(","); - } - builder.append(s); - } - } - return builder.toString(); - } - - private static boolean containsFunction(String functions, String function) { - return Arrays.asList(functions.split(",")).contains(function); - } - private final class UsbHandler extends Handler { // current USB state @@ -317,49 +301,24 @@ public class UsbDeviceManager { private boolean mConfigured; private boolean mUsbDataUnlocked; private String mCurrentFunctions; + private boolean mCurrentFunctionsApplied; private UsbAccessory mCurrentAccessory; private int mUsbNotificationId; - private String mDefaultFunctions; private boolean mAdbNotificationShown; private int mCurrentUser = UserHandle.USER_NULL; - private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG) Slog.d(TAG, "boot completed"); - mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); - } - }; - - private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget(); - } - }; - public UsbHandler(Looper looper) { super(looper); try { - // TODO: rename persist.sys.usb.config to something more descriptive. - // persist.sys.usb.config should never be unset. But if it is, set it to "adb" - // so we have a chance of debugging what happened. - mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb"); - - // sanity check the sys.usb.config system property - // this may be necessary if we crashed while switching USB configurations - String config = SystemProperties.get("sys.usb.config", "none"); - if (!config.equals(mDefaultFunctions)) { - Slog.w(TAG, "resetting config to persistent property: " + mDefaultFunctions); - SystemProperties.set("sys.usb.config", mDefaultFunctions); - } - - mAdbEnabled = containsFunction( - SystemProperties.get(UsbManager.ADB_PERSISTENT_PROPERTY, "adb"), + // Restore default functions. + mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY, + UsbManager.USB_FUNCTION_NONE); + mCurrentFunctionsApplied = mCurrentFunctions.equals( + SystemProperties.get(USB_STATE_PROPERTY)); + mAdbEnabled = UsbManager.containsFunction(getDefaultFunctions(), UsbManager.USB_FUNCTION_ADB); + setEnabledFunctions(null, false); - mCurrentFunctions = getDefaultFunctions(); String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); @@ -371,12 +330,6 @@ public class UsbDeviceManager { // Watch for USB configuration changes mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); - - IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiver(mBootCompletedReceiver, filter); - mContext.registerReceiver( - mUserSwitchedReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); } catch (Exception e) { Slog.e(TAG, "Error initializing UsbHandler", e); } @@ -420,34 +373,26 @@ public class UsbDeviceManager { sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0); } - private void updatePersistentProperty() { - String newValue = getDefaultFunctions(); - String value = SystemProperties.get(UsbManager.ADB_PERSISTENT_PROPERTY); - if (DEBUG) { Slog.d(TAG, "updatePersistentProperty newValue=" + newValue + " value=" + value); } - if (!newValue.equals(value)) { - SystemProperties.set(UsbManager.ADB_PERSISTENT_PROPERTY, getDefaultFunctions()); - } - waitForState(newValue); - } - private boolean waitForState(String state) { // wait for the transition to complete. // give up after 1 second. + String value = null; for (int i = 0; i < 20; i++) { // State transition is done when sys.usb.state is set to the new configuration - if (state.equals(SystemProperties.get("sys.usb.state"))) return true; + value = SystemProperties.get(USB_STATE_PROPERTY); + if (state.equals(value)) return true; SystemClock.sleep(50); } - Slog.e(TAG, "waitForState(" + state + ") FAILED"); + Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value); return false; } private boolean setUsbConfig(String config) { if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); // set the new configuration - String oldConfig = SystemProperties.get(UsbManager.USB_SETTINGS_PROPERTY); + String oldConfig = SystemProperties.get(USB_CONFIG_PROPERTY); if (!config.equals(oldConfig)) { - SystemProperties.set(UsbManager.USB_SETTINGS_PROPERTY, config); + SystemProperties.set(USB_CONFIG_PROPERTY, config); } return waitForState(config); } @@ -456,54 +401,110 @@ public class UsbDeviceManager { if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; + // Due to the persist.sys.usb.config property trigger, changing adb state requires // persisting default function - updatePersistentProperty(); + String oldFunctions = getDefaultFunctions(); + String newFunctions = applyAdbFunction(oldFunctions); + if (!oldFunctions.equals(newFunctions)) { + SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunctions); + } + // After persisting them use the lock-down aware function set - setEnabledFunctions(getDefaultFunctions()); + setEnabledFunctions(mCurrentFunctions, false); updateAdbNotification(); } + if (mDebuggingManager != null) { mDebuggingManager.setAdbEnabled(mAdbEnabled); } } /** - * Stop and start the USB driver. This is needed to close all outstanding - * USB connections. + * Evaluates USB function policies and applies the change accordingly. */ - private void restartCurrentFunction() { - setUsbConfig("none"); - setUsbConfig(mCurrentFunctions); - } + private void setEnabledFunctions(String functions, boolean forceRestart) { + if (DEBUG) Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", " + + "forceRestart=" + forceRestart); + + // Try to set the enabled functions. + final String oldFunctions = mCurrentFunctions; + final boolean oldFunctionsApplied = mCurrentFunctionsApplied; + if (trySetEnabledFunctions(functions, forceRestart)) { + return; + } - private void setEnabledFunctions(String functions) { - if (DEBUG) Slog.d(TAG, "setEnabledFunctions " + functions); + // Didn't work. Try to revert changes. + // We always reapply the policy in case certain constraints changed such as + // user restrictions independently of any other new functions we were + // trying to activate. + if (oldFunctionsApplied && !oldFunctions.equals(functions)) { + Slog.e(TAG, "Failsafe 1: Restoring previous USB functions."); + if (trySetEnabledFunctions(oldFunctions, false)) { + return; + } + } + // Still didn't work. Try to restore the default functions. + Slog.e(TAG, "Failsafe 2: Restoring default USB functions."); + if (trySetEnabledFunctions(null, false)) { + return; + } + + // Now we're desperate. Ignore the default functions. + // Try to get ADB working if enabled. + Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled)."); + if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) { + return; + } + + // Ouch. + Slog.e(TAG, "Unable to set any USB functions!"); + } + + private boolean trySetEnabledFunctions(String functions, boolean forceRestart) { if (functions == null) { functions = getDefaultFunctions(); } + functions = applyAdbFunction(functions); + functions = applyUserRestrictions(functions); + + if (!mCurrentFunctions.equals(functions) || !mCurrentFunctionsApplied + || forceRestart) { + Slog.i(TAG, "Setting USB config to " + functions); + mCurrentFunctions = functions; + mCurrentFunctionsApplied = false; + + // Kick the USB stack to close existing connections. + setUsbConfig(UsbManager.USB_FUNCTION_NONE); + + // Set the new USB configuration. + if (!setUsbConfig(functions)) { + Slog.e(TAG, "Failed to switch USB config to " + functions); + return false; + } + + mCurrentFunctionsApplied = true; + } + return true; + } + private String applyAdbFunction(String functions) { if (mAdbEnabled) { - functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB); + functions = UsbManager.addFunction(functions, UsbManager.USB_FUNCTION_ADB); } else { - functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB); + functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_ADB); } - if (!mCurrentFunctions.equals(functions)) { - if (!setUsbConfig("none")) { - Slog.e(TAG, "Failed to disable USB"); - // revert to previous configuration if we fail - setUsbConfig(mCurrentFunctions); - return; - } - if (setUsbConfig(functions)) { - mCurrentFunctions = functions; - } else { - Slog.e(TAG, "Failed to switch USB config to " + functions); - // revert to previous configuration if we fail - setUsbConfig(mCurrentFunctions); - } + return functions; + } + + private String applyUserRestrictions(String functions) { + UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { + functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_MTP); + functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_PTP); } + return functions; } private void updateCurrentAccessory() { @@ -523,7 +524,7 @@ public class UsbDeviceManager { // defer accessoryAttached if system is not ready if (mBootCompleted) { getCurrentSettings().accessoryAttached(mCurrentAccessory); - } // else handle in mBootCompletedReceiver + } // else handle in boot completed } else { Slog.e(TAG, "nativeGetAccessoryStrings failed"); } @@ -531,7 +532,7 @@ public class UsbDeviceManager { // make sure accessory mode is off // and restore default functions Slog.d(TAG, "exited USB accessory mode"); - setEnabledFunctions(getDefaultFunctions()); + setEnabledFunctions(null, false); if (mCurrentAccessory != null) { if (mBootCompleted) { @@ -543,10 +544,11 @@ public class UsbDeviceManager { } } - private void updateUsbState() { + private void updateUsbStateBroadcast() { // send a sticky broadcast containing current USB state Intent intent = new Intent(UsbManager.ACTION_USB_STATE); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(UsbManager.USB_CONNECTED, mConnected); intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured); intent.putExtra(UsbManager.USB_DATA_UNLOCKED, mUsbDataUnlocked); @@ -563,8 +565,13 @@ public class UsbDeviceManager { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } + private void updateUsbFunctions() { + updateAudioSourceFunction(); + updateMidiFunction(); + } + private void updateAudioSourceFunction() { - boolean enabled = containsFunction(mCurrentFunctions, + boolean enabled = UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_AUDIO_SOURCE); if (enabled != mAudioSourceEnabled) { int card = -1; @@ -590,7 +597,8 @@ public class UsbDeviceManager { } private void updateMidiFunction() { - boolean enabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI); + boolean enabled = UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MIDI); if (enabled != mMidiEnabled) { if (enabled) { Scanner scanner = null; @@ -624,17 +632,16 @@ public class UsbDeviceManager { } updateUsbNotification(); updateAdbNotification(); - if (containsFunction(mCurrentFunctions, + if (UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); } else if (!mConnected) { // restore defaults when USB is disconnected - setEnabledFunctions(getDefaultFunctions()); + setEnabledFunctions(null, false); } if (mBootCompleted) { - updateUsbState(); - updateAudioSourceFunction(); - updateMidiFunction(); + updateUsbStateBroadcast(); + updateUsbFunctions(); } break; case MSG_ENABLE_ADB: @@ -642,26 +649,25 @@ public class UsbDeviceManager { break; case MSG_SET_CURRENT_FUNCTIONS: String functions = (String)msg.obj; - setEnabledFunctions(functions); + setEnabledFunctions(functions, false); + break; + case MSG_UPDATE_USER_RESTRICTIONS: + setEnabledFunctions(mCurrentFunctions, false); break; case MSG_SET_USB_DATA_UNLOCKED: mUsbDataUnlocked = (msg.arg1 == 1); updateUsbNotification(); - updateUsbState(); - restartCurrentFunction(); + updateUsbStateBroadcast(); + setEnabledFunctions(mCurrentFunctions, true); break; case MSG_SYSTEM_READY: - setUsbConfig(mCurrentFunctions); - updatePersistentProperty(); updateUsbNotification(); updateAdbNotification(); - updateUsbState(); - updateAudioSourceFunction(); - updateMidiFunction(); + updateUsbStateBroadcast(); + updateUsbFunctions(); break; case MSG_BOOT_COMPLETED: mBootCompleted = true; - setUsbConfig(mCurrentFunctions); if (mCurrentAccessory != null) { getCurrentSettings().accessoryAttached(mCurrentAccessory); } @@ -670,27 +676,19 @@ public class UsbDeviceManager { } break; case MSG_USER_SWITCHED: { - UserManager userManager = - (UserManager) mContext.getSystemService(Context.USER_SERVICE); - UserHandle userHandle = new UserHandle(msg.arg1); - if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, - userHandle)) { - Slog.v(TAG, "Switched to user " + msg.arg1 + - " with DISALLOW_USB_FILE_TRANSFER restriction; disabling USB."); - setUsbConfig("none"); + if (mCurrentUser != msg.arg1) { + // Restart the USB stack and re-apply user restrictions for MTP or PTP. + final boolean active = UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MTP) + || UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_PTP); + if (active && mCurrentUser != UserHandle.USER_NULL) { + Slog.v(TAG, "Current user switched to " + mCurrentUser + + "; resetting USB host stack for MTP or PTP"); + setEnabledFunctions(mCurrentFunctions, true); + } mCurrentUser = msg.arg1; - break; - } - - final boolean mtpActive = - containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP) - || containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP); - if (mtpActive && mCurrentUser != UserHandle.USER_NULL) { - Slog.v(TAG, "Current user switched; resetting USB host stack for MTP"); - setUsbConfig("none"); - setUsbConfig(mCurrentFunctions); } - mCurrentUser = msg.arg1; break; } } @@ -707,16 +705,17 @@ public class UsbDeviceManager { if (mConnected) { if (!mUsbDataUnlocked) { id = com.android.internal.R.string.usb_charging_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MTP)) { id = com.android.internal.R.string.usb_mtp_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_PTP)) { id = com.android.internal.R.string.usb_ptp_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MIDI)) { id = com.android.internal.R.string.usb_midi_notification_title; - } else if (containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_MASS_STORAGE)) { - id = com.android.internal.R.string.usb_cd_installer_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_ACCESSORY)) { id = com.android.internal.R.string.usb_accessory_notification_title; } else { id = com.android.internal.R.string.usb_charging_notification_title; @@ -804,17 +803,14 @@ public class UsbDeviceManager { } private String getDefaultFunctions() { - UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE); - if(userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, - new UserHandle(mCurrentUser))) { - return "none"; - } - return mDefaultFunctions; + return SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, + UsbManager.USB_FUNCTION_ADB); } public void dump(FileDescriptor fd, PrintWriter pw) { pw.println(" USB Device State:"); - pw.println(" Current Functions: " + mCurrentFunctions); + pw.println(" mCurrentFunctions: " + mCurrentFunctions); + pw.println(" mCurrentFunctionsApplied: " + mCurrentFunctionsApplied); pw.println(" mConnected: " + mConnected); pw.println(" mConfigured: " + mConfigured); pw.println(" mCurrentAccessory: " + mCurrentAccessory); @@ -850,6 +846,10 @@ public class UsbDeviceManager { return nativeOpenAccessory(); } + public boolean isFunctionEnabled(String function) { + return UsbManager.containsFunction(SystemProperties.get(USB_CONFIG_PROPERTY), function); + } + public void setCurrentFunctions(String functions) { if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ")"); mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions); diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 987a79f..c8b4226 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -17,6 +17,7 @@ package com.android.server.usb; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -25,10 +26,12 @@ import android.content.pm.PackageManager; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.os.UserManager; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -63,6 +66,8 @@ public class UsbService extends IUsbManager.Stub { public void onBootPhase(int phase) { if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mUsbService.systemReady(); + } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { + mUsbService.bootCompleted(); } } } @@ -108,13 +113,15 @@ public class UsbService extends IUsbManager.Stub { setCurrentUser(UserHandle.USER_OWNER); - final IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_SWITCHED); - userFilter.addAction(Intent.ACTION_USER_STOPPED); - mContext.registerReceiver(mUserReceiver, userFilter, null, null); + final IntentFilter filter = new IntentFilter(); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_USER_STOPPED); + filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mContext.registerReceiver(mReceiver, filter, null, null); } - private BroadcastReceiver mUserReceiver = new BroadcastReceiver() { + private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); @@ -125,6 +132,11 @@ public class UsbService extends IUsbManager.Stub { synchronized (mLock) { mSettingsByUser.remove(userId); } + } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED + .equals(action)) { + if (mDeviceManager != null) { + mDeviceManager.updateUserRestrictions(); + } } } }; @@ -135,7 +147,7 @@ public class UsbService extends IUsbManager.Stub { mHostManager.setCurrentSettings(userSettings); } if (mDeviceManager != null) { - mDeviceManager.setCurrentSettings(userSettings); + mDeviceManager.setCurrentUser(userId, userSettings); } } @@ -150,6 +162,12 @@ public class UsbService extends IUsbManager.Stub { } } + public void bootCompleted() { + if (mDeviceManager != null) { + mDeviceManager.bootCompleted(); + } + } + /* Returns a list of all currently attached USB devices (host mdoe) */ @Override public void getDeviceList(Bundle devices) { @@ -252,15 +270,19 @@ public class UsbService extends IUsbManager.Stub { } @Override + public boolean isFunctionEnabled(String function) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + return mDeviceManager != null && mDeviceManager.isFunctionEnabled(function); + } + + @Override public void setCurrentFunction(String function) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - // If attempt to change USB function while file transfer is restricted, ensure that - // the current function is set to "none", and return. - UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { - if (mDeviceManager != null) mDeviceManager.setCurrentFunctions("none"); - return; + if (!isSupportedCurrentFunction(function)) { + Slog.w(TAG, "Caller of setCurrentFunction() requested unsupported USB function: " + + function); + function = UsbManager.USB_FUNCTION_NONE; } if (mDeviceManager != null) { @@ -270,6 +292,22 @@ public class UsbService extends IUsbManager.Stub { } } + private static boolean isSupportedCurrentFunction(String function) { + if (function == null) return true; + + switch (function) { + case UsbManager.USB_FUNCTION_NONE: + case UsbManager.USB_FUNCTION_AUDIO_SOURCE: + case UsbManager.USB_FUNCTION_MIDI: + case UsbManager.USB_FUNCTION_MTP: + case UsbManager.USB_FUNCTION_PTP: + case UsbManager.USB_FUNCTION_RNDIS: + return true; + } + + return false; + } + @Override public void setUsbDataUnlocked(boolean unlocked) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 61ae1c0..36478da 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -761,7 +761,8 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void showSessionForActiveService(IVoiceInteractionSessionShowCallback showCallback) { + public void showSessionForActiveService(Bundle args, + IVoiceInteractionSessionShowCallback showCallback) { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); synchronized (this) { if (mImpl == null) { @@ -771,7 +772,7 @@ public class VoiceInteractionManagerService extends SystemService { } final long caller = Binder.clearCallingIdentity(); try { - mImpl.showSessionLocked(new Bundle() /* sessionArgs */, + mImpl.showSessionLocked(args, VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE | VoiceInteractionSession.SHOW_WITH_ASSIST | VoiceInteractionSession.SHOW_WITH_SCREENSHOT, |