diff options
Diffstat (limited to 'services')
14 files changed, 619 insertions, 312 deletions
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 8ca075f..daac521 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 }; @@ -569,8 +559,9 @@ public class ConnectivityService extends IConnectivityManager.Stub final DetailedState state = nai.networkInfo.getDetailedState(); for (int type = 0; type < mTypeLists.length; type++) { final ArrayList<NetworkAgentInfo> list = mTypeLists[type]; + final boolean contains = (list != null && list.contains(nai)); final boolean isFirst = (list != null && list.size() > 0 && nai == list.get(0)); - if (isFirst || isDefault) { + if (isFirst || (contains && isDefault)) { maybeLogBroadcast(nai, state, type, isDefault); sendLegacyNetworkBroadcast(nai, state, type); } @@ -890,7 +881,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) { @@ -1795,7 +1786,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"); @@ -1920,8 +1911,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; } @@ -2011,11 +2001,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); } @@ -2046,8 +2034,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); @@ -2066,17 +2053,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); } @@ -2147,33 +2139,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) { @@ -2182,11 +2154,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). @@ -2240,49 +2208,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); } } @@ -2295,43 +2223,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) { @@ -2441,7 +2354,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); } @@ -2750,16 +2663,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 @@ -3129,11 +3032,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(); } } @@ -4050,7 +3953,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); } @@ -4085,31 +3988,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); } } @@ -4251,7 +4152,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) @@ -4264,17 +4165,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; @@ -4287,7 +4183,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."); } @@ -4344,10 +4240,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); } } @@ -4381,7 +4284,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); } @@ -4432,19 +4335,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); } @@ -4463,10 +4357,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 @@ -4475,17 +4367,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); } } @@ -4573,8 +4469,7 @@ 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); @@ -4594,8 +4489,7 @@ public class ConnectivityService extends IConnectivityManager.Stub state == NetworkInfo.State.SUSPENDED) { // going into or coming out of SUSPEND: rescore and notify if (networkAgent.getCurrentScore() != oldScore) { - rematchAllNetworksAndRequests(networkAgent, oldScore, - NascentState.NOT_JUST_VALIDATED); + rematchAllNetworksAndRequests(networkAgent, oldScore); } notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ? ConnectivityManager.CALLBACK_SUSPENDED : @@ -4615,7 +4509,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); } @@ -4665,7 +4559,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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 88a0c8f..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); } } @@ -10753,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); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 31cdcd5..470bbb0 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2669,8 +2669,13 @@ final class ActivityStack { if (!r.finishing) { if (!mService.isSleeping()) { if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r); - requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, - "stop-no-history", false); + if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, + "stop-no-history", false)) { + // Activity was finished, no need to continue trying to schedule stop. + adjustFocusedActivityLocked(r, "stopActivityFinished"); + r.resumeKeyDispatchingLocked(); + return; + } } else { if (DEBUG_STATES) Slog.d(TAG_STATES, "Not finishing noHistory " + r + " on stop because we're just sleeping"); 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 3c117aa..13aca79 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; @@ -2053,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()) { @@ -2086,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; @@ -2160,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"); @@ -2198,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. @@ -3147,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. @@ -3429,12 +3458,17 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } + // 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)) { final long token = Binder.clearCallingIdentity(); try { - final StorageManager storage = mContext.getSystemService(StorageManager.class); - storage.remountUid(uid); + if (sUserManager.isInitialized(userId)) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + storage.remountUid(uid); + } } finally { Binder.restoreCallingIdentity(token); } @@ -4398,8 +4432,11 @@ public class PackageManagerService extends IPackageManager.Stub { // cross-profile app linking works only towards the parent. final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { - return getCrossProfileDomainPreferredLpr(intent, resolvedType, 0, sourceUserId, - parent.id) != null; + CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( + intent, resolvedType, 0, sourceUserId, parent.id); + return xpDomainInfo != null + && xpDomainInfo.bestDomainVerificationStatus != + INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; } } return false; @@ -4495,7 +4532,7 @@ public class PackageManagerService extends IPackageManager.Stub { return result; } result = filterCandidatesWithDomainPreferredActivitiesLPr(flags, result, - xpDomainInfo); + xpDomainInfo, userId); Collections.sort(result, mResolvePrioritySorter); } return result; @@ -4612,13 +4649,13 @@ public class PackageManagerService extends IPackageManager.Stub { } private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr( - int flags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo) { + int flags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo, + int userId) { if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { - Slog.v("TAG", "Filtering results with preferred activities. Candidates count: " + + Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } - final int userId = UserHandle.getCallingUserId(); ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>(); ArrayList<ResolveInfo> alwaysList = new ArrayList<ResolveInfo>(); ArrayList<ResolveInfo> undefinedList = new ArrayList<ResolveInfo>(); @@ -5633,7 +5670,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)) { @@ -5646,7 +5683,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 @@ -6429,20 +6469,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."); + } } } } @@ -15924,16 +15973,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } - 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/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/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(); |