diff options
Diffstat (limited to 'services')
9 files changed, 352 insertions, 152 deletions
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 083aa9b..f6e2e67 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1234,6 +1234,7 @@ public class BackupManagerService extends IBackupManager.Stub { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); // Register for events related to sdcard installation. @@ -1688,11 +1689,12 @@ public class BackupManagerService extends IBackupManager.Stub { String action = intent.getAction(); boolean replacing = false; boolean added = false; - boolean rebind = false; + boolean changed = false; Bundle extras = intent.getExtras(); String pkgList[] = null; if (Intent.ACTION_PACKAGE_ADDED.equals(action) || - Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + Intent.ACTION_PACKAGE_REMOVED.equals(action) || + Intent.ACTION_PACKAGE_CHANGED.equals(action)) { Uri uri = intent.getData(); if (uri == null) { return; @@ -1701,7 +1703,43 @@ public class BackupManagerService extends IBackupManager.Stub { if (pkgName != null) { pkgList = new String[] { pkgName }; } - rebind = added = Intent.ACTION_PACKAGE_ADDED.equals(action); + changed = Intent.ACTION_PACKAGE_CHANGED.equals(action); + + // At package-changed we only care about looking at new transport states + if (changed) { + try { + if (MORE_DEBUG) { + Slog.i(TAG, "Package " + pkgName + " changed; rechecking"); + } + // unbind existing possibly-stale connections to that package's transports + synchronized (mTransports) { + TransportConnection conn = mTransportConnections.get(pkgName); + if (conn != null) { + final ServiceInfo svc = conn.mTransport; + ComponentName svcName = + new ComponentName(svc.packageName, svc.name); + String flatName = svcName.flattenToShortString(); + Slog.i(TAG, "Unbinding " + svcName); + + mContext.unbindService(conn); + mTransportConnections.remove(pkgName); + mTransports.remove(mTransportNames.get(flatName)); + mTransportNames.remove(flatName); + } + } + // and then (re)bind as appropriate + PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0); + checkForTransportAndBind(app); + } catch (NameNotFoundException e) { + // Nope, can't find it - just ignore + if (MORE_DEBUG) { + Slog.w(TAG, "Can't find changed package " + pkgName); + } + } + return; // nothing more to do in the PACKAGE_CHANGED case + } + + added = Intent.ACTION_PACKAGE_ADDED.equals(action); replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { added = true; @@ -1737,17 +1775,15 @@ public class BackupManagerService extends IBackupManager.Stub { // Transport maintenance: rebind to known existing transports that have // just been updated; and bind to any newly-installed transport services. - if (rebind) { - synchronized (mTransportConnections) { - final TransportConnection conn = mTransportConnections.get(packageName); - if (conn != null) { - if (DEBUG) { - Slog.i(TAG, "Transport package changed; rebinding"); - } - bindTransport(conn.mTransport); - } else { - checkForTransportAndBind(app); + synchronized (mTransports) { + final TransportConnection conn = mTransportConnections.get(packageName); + if (conn != null) { + if (MORE_DEBUG) { + Slog.i(TAG, "Transport package changed; rebinding"); } + bindTransport(conn.mTransport); + } else { + checkForTransportAndBind(app); } } @@ -1840,7 +1876,7 @@ public class BackupManagerService extends IBackupManager.Stub { intent.setComponent(svcName); TransportConnection connection; - synchronized (mTransportConnections) { + synchronized (mTransports) { connection = mTransportConnections.get(transport.packageName); if (null == connection) { connection = new TransportConnection(transport); @@ -8462,31 +8498,24 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF return list; } - // Select which transport to use for the next backup operation. If the given - // name is not one of the available transports, no action is taken and the method - // returns null. + // Select which transport to use for the next backup operation. public String selectBackupTransport(String transport) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport"); synchronized (mTransports) { - String prevTransport = null; - if (mTransports.get(transport) != null) { - final long oldId = Binder.clearCallingIdentity(); - try { - prevTransport = mCurrentTransport; - mCurrentTransport = transport; - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.BACKUP_TRANSPORT, transport); - } finally { - Binder.restoreCallingIdentity(oldId); - } + final long oldId = Binder.clearCallingIdentity(); + try { + String prevTransport = mCurrentTransport; + mCurrentTransport = transport; + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT, transport); Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport + " returning " + prevTransport); - } else { - Slog.w(TAG, "Attempt to select unavailable transport " + transport); + return prevTransport; + } finally { + Binder.restoreCallingIdentity(oldId); } - return prevTransport; } } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index ef6e07c..c3465d1 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -631,7 +631,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkPackage(int uid, String packageName) { synchronized (this) { - if (getOpsLocked(uid, packageName, true) != null) { + if (getOpsRawLocked(uid, packageName, true) != null) { return AppOpsManager.MODE_ALLOWED; } else { return AppOpsManager.MODE_ERRORED; @@ -769,6 +769,15 @@ public class AppOpsService extends IAppOpsService.Stub { } private Ops getOpsLocked(int uid, String packageName, boolean edit) { + if (uid == 0) { + packageName = "root"; + } else if (uid == Process.SHELL_UID) { + packageName = "com.android.shell"; + } + return getOpsRawLocked(uid, packageName, edit); + } + + private Ops getOpsRawLocked(int uid, String packageName, boolean edit) { HashMap<String, Ops> pkgOps = mUidOps.get(uid); if (pkgOps == null) { if (!edit) { @@ -777,11 +786,6 @@ public class AppOpsService extends IAppOpsService.Stub { pkgOps = new HashMap<String, Ops>(); mUidOps.put(uid, pkgOps); } - if (uid == 0) { - packageName = "root"; - } else if (uid == Process.SHELL_UID) { - packageName = "com.android.shell"; - } Ops ops = pkgOps.get(packageName); if (ops == null) { if (!edit) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 75090db..55d8c09 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -251,7 +251,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private Context mContext; private int mNetworkPreference; - private int mActiveDefaultNetwork = -1; + private int mActiveDefaultNetwork = TYPE_NONE; // 0 is full bad, 100 is full good private int mDefaultInetConditionPublished = 0; @@ -886,15 +886,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { return getNetworkInfo(mActiveDefaultNetwork, uid); } - // only called when the default request is satisfied - private void updateActiveDefaultNetwork(NetworkAgentInfo nai) { - if (nai != null) { - mActiveDefaultNetwork = nai.networkInfo.getType(); - } else { - mActiveDefaultNetwork = TYPE_NONE; - } - } - /** * Find the first Provisioning network. * @@ -1794,6 +1785,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) { + if (nai.network == null) return false; final NetworkAgentInfo officialNai; synchronized (mNetworkForNetId) { officialNai = mNetworkForNetId.get(nai.network.netId); @@ -1933,10 +1925,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { } break; } - case NetworkMonitor.EVENT_NETWORK_VALIDATED: { + case NetworkMonitor.EVENT_NETWORK_TESTED: { NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj; if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) { - handleConnectionValidated(nai); + boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID); + if (valid) { + if (DBG) log("Validated " + nai.name()); + nai.validated = true; + rematchNetworkAndRequests(nai); + } + updateInetCondition(nai, valid); } break; } @@ -2057,7 +2055,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (nri.isRequest == false) continue; NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, - (nai != null ? nai.currentScore : 0), 0, nri.request); + (nai != null ? nai.getCurrentScore() : 0), 0, nri.request); } } else { loge("Error connecting NetworkFactory"); @@ -2136,7 +2134,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { request.networkCapabilities.satisfiedByNetworkCapabilities( existing.networkCapabilities) && (alternative == null || - alternative.currentScore < existing.currentScore)) { + alternative.getCurrentScore() < existing.getCurrentScore())) { alternative = existing; } } @@ -2169,8 +2167,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { for (NetworkAgentInfo network : mNetworkAgentInfos.values()) { if (DBG) log("handleRegisterNetworkRequest checking " + network.name()); if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) { - if (DBG) log("apparently satisfied. currentScore=" + network.currentScore); - if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) { + if (DBG) log("apparently satisfied. currentScore=" + network.getCurrentScore()); + if ((bestNetwork == null) || + bestNetwork.getCurrentScore() < 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 @@ -2194,7 +2193,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { bestNetwork.addRequest(nri.request); mNetworkForRequestId.put(nri.request.requestId, bestNetwork); notifyNetworkCallback(bestNetwork, nri); - score = bestNetwork.currentScore; + score = bestNetwork.getCurrentScore(); if (nri.request.legacyType != TYPE_NONE) { mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork); } @@ -4516,19 +4515,34 @@ public class ConnectivityService extends IConnectivityManager.Stub { updateTcpBufferSizes(newNetwork); } - private void handleConnectionValidated(NetworkAgentInfo newNetwork) { - if (newNetwork == null) { - loge("Unknown NetworkAgentInfo in handleConnectionValidated"); - return; - } - if (newNetwork.validated) return; - newNetwork.validated = true; + // Handles a network appearing or improving its score. + // + // - Evaluates all current NetworkRequests that can be + // satisfied by newNetwork, and reassigns to newNetwork + // any such requests for which newNetwork is the best. + // + // - Tears down any Networks that as a result are no longer + // needed. A network is needed if it is the best network for + // one or more NetworkRequests, or if it is a VPN. + // + // - Tears down newNetwork if it is validated but turns out to be + // unneeded. Does not tear down newNetwork if it is + // unvalidated, because future validation may improve + // newNetwork's score enough that it is needed. + // + // NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy, + // it does not remove NetworkRequests that other Networks could better satisfy. + // If you need to handle decreases in score, use {@link rematchAllNetworksAndRequests}. + // This function should be used when possible instead of {@code rematchAllNetworksAndRequests} + // as it performs better by a factor of the number of Networks. + private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork) { boolean keep = newNetwork.isVPN(); boolean isNewDefault = false; - if (DBG) log("handleConnectionValidated for "+newNetwork.name()); - // check if any NetworkRequest wants this NetworkAgent + if (DBG) log("rematching " + newNetwork.name()); + // Find and migrate to this Network any NetworkRequests for + // which this network is now the best. ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>(); - if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities); + if (VDBG) log(" network has: " + newNetwork.networkCapabilities); for (NetworkRequestInfo nri : mNetworkRequests.values()) { NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); if (newNetwork == currentNetwork) { @@ -4543,18 +4557,21 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities( newNetwork.networkCapabilities)) { if (!nri.isRequest) { + // This is not a request, it's a callback listener. + // Add it to newNetwork regardless of score. newNetwork.addRequest(nri.request); continue; } + // next check if it's better than any current network we're using for // this request if (VDBG) { log("currentScore = " + - (currentNetwork != null ? currentNetwork.currentScore : 0) + - ", newScore = " + newNetwork.currentScore); + (currentNetwork != null ? currentNetwork.getCurrentScore() : 0) + + ", newScore = " + newNetwork.getCurrentScore()); } if (currentNetwork == null || - currentNetwork.currentScore < newNetwork.currentScore) { + currentNetwork.getCurrentScore() < newNetwork.getCurrentScore()) { if (currentNetwork != null) { if (DBG) log(" accepting network in place of " + currentNetwork.name()); currentNetwork.networkRequests.remove(nri.request.requestId); @@ -4569,13 +4586,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { mLegacyTypeTracker.add(nri.request.legacyType, newNetwork); } keep = true; + // Tell NetworkFactories about the new score, so they can stop + // trying to connect if they know they cannot match it. // TODO - this could get expensive if we have alot of requests for this // network. Think about if there is a way to reduce this. Push // netid->request mapping to each factory? - sendUpdatedScoreToFactories(nri.request, newNetwork.currentScore); + sendUpdatedScoreToFactories(nri.request, newNetwork.getCurrentScore()); if (mDefaultRequest.requestId == nri.request.requestId) { isNewDefault = true; - updateActiveDefaultNetwork(newNetwork); + // TODO: Remove following line. It's redundant with makeDefault call. + mActiveDefaultNetwork = newNetwork.networkInfo.getType(); if (newNetwork.linkProperties != null) { updateTcpBufferSizes(newNetwork); setDefaultDnsSystemProperties( @@ -4591,12 +4611,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { mLegacyTypeTracker.remove(currentNetwork.networkInfo.getType(), currentNetwork); } - mDefaultInetConditionPublished = 100; + mDefaultInetConditionPublished = newNetwork.validated ? 100 : 0; mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork); } } } } + // Linger any networks that are no longer needed. for (NetworkAgentInfo nai : affectedNetworks) { boolean teardown = !nai.isVPN(); for (int i = 0; i < nai.networkRequests.size() && teardown; i++) { @@ -4624,6 +4645,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } if (keep) { if (isNewDefault) { + // Notify system services that this network is up. makeDefault(newNetwork); synchronized (ConnectivityService.this) { // have a new default network, release the transition wakelock in @@ -4640,6 +4662,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // Notify battery stats service about this network, both the normal // interface and any stacked links. + // TODO: Avoid redoing this; this must only be done once when a network comes online. try { final IBatteryStats bs = BatteryStatsService.getService(); final int type = newNetwork.networkInfo.getType(); @@ -4655,7 +4678,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { } notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE); - } else { + } else if (newNetwork.validated) { + // Only tear down validated networks here. Leave unvalidated to either become + // validated (and get evaluated against peers, one losing here) or + // NetworkMonitor reports a bad network and we tear it down then. + // TODO: Could teardown unvalidated networks when their NetworkCapabilities + // satisfy no NetworkRequests. if (DBG && newNetwork.networkRequests.size() != 0) { loge("tearing down network with live requests:"); for (int i=0; i < newNetwork.networkRequests.size(); i++) { @@ -4667,6 +4695,46 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + // Attempt to rematch all Networks with NetworkRequests. This may result in Networks + // being disconnected. + // If only one Network's score or capabilities have been modified since the last time + // this function was called, pass this Network in via the "changed" arugment, otherwise + // pass null. + // If only one Network has been changed but its NetworkCapabilities have not changed, + // pass in the Network's score (from getCurrentScore()) prior to the change via + // "oldScore", otherwise pass changed.getCurrentScore() or 0 if "changed" is null. + 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 + // satifying a NetworkRequest that "changed" currently satisfies. + + // 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()) { + rematchNetworkAndRequests(changed); + } else { + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + rematchNetworkAndRequests(nai); + } + } + } + + private void updateInetCondition(NetworkAgentInfo nai, boolean valid) { + // Don't bother updating until we've graduated to validated at least once. + if (!nai.validated) return; + // For now only update icons for default connection. + // TODO: Update WiFi and cellular icons separately. b/17237507 + if (!isDefaultNetwork(nai)) return; + + int newInetCondition = valid ? 100 : 0; + // Don't repeat publish. + if (newInetCondition == mDefaultInetConditionPublished) return; + + mDefaultInetConditionPublished = newInetCondition; + sendInetConditionBroadcast(nai.networkInfo); + } private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) { NetworkInfo.State state = newInfo.getState(); @@ -4721,12 +4789,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { } // TODO: support proxy per network. } - // Make default network if we have no default. Any network is better than no network. + // Consider network even though it is not yet validated. + // TODO: All the if-statement conditions can be removed now that validation only confers + // a score increase. if (mNetworkForRequestId.get(mDefaultRequest.requestId) == null && networkAgent.isVPN() == false && mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities( networkAgent.networkCapabilities)) { - makeDefault(networkAgent); + rematchNetworkAndRequests(networkAgent); } } else if (state == NetworkInfo.State.DISCONNECTED || state == NetworkInfo.State.SUSPENDED) { @@ -4752,23 +4822,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { score = 0; } - nai.currentScore = score; - - // TODO - This will not do the right thing if this network is lowering - // its score and has requests that can be served by other - // currently-active networks, or if the network is increasing its - // score and other networks have requests that can be better served - // by this network. - // - // Really we want to see if any of our requests migrate to other - // active/lingered networks and if any other requests migrate to us (depending - // on increasing/decreasing currentScore. That's a bit of work and probably our - // score checking/network allocation code needs to be modularized so we can understand - // (see handleConnectionValided for an example). - // - // As a first order approx, lets just advertise the new score to factories. If - // somebody can beat it they will nominate a network and our normal net replacement - // code will fire. + final int oldScore = nai.getCurrentScore(); + nai.setCurrentScore(score); + + if (nai.created) rematchAllNetworksAndRequests(nai, oldScore); + for (int i = 0; i < nai.networkRequests.size(); i++) { NetworkRequest nr = nai.networkRequests.valueAt(i); // Don't send listening requests to factories. b/17393458 diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index 2896f60..6f378fd 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -17,6 +17,7 @@ package com.android.server; import android.Manifest; +import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; @@ -26,7 +27,9 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.service.persistentdata.IPersistentDataBlockService; import android.util.Slog; + import com.android.internal.R; + import libcore.io.IoUtils; import java.io.DataInputStream; @@ -241,6 +244,10 @@ public class PersistentDataBlockService extends SystemService { @Override public void setOemUnlockEnabled(boolean enabled) { + // do not allow monkey to flip the flag + if (ActivityManager.isUserAMonkey()) { + return; + } enforceOemUnlockPermission(); FileOutputStream outputStream; try { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2885b83..2c39691 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -413,7 +413,7 @@ public final class ActivityManagerService extends ActivityManagerNative * List of intents that were used to start the most recent tasks. */ ArrayList<TaskRecord> mRecentTasks; - ArraySet<TaskRecord> mTmpRecents = new ArraySet<TaskRecord>(); + ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>(); /** * For addAppTask: cached of the last activity component that was added. @@ -3857,6 +3857,86 @@ public final class ActivityManagerService extends ActivityManagerNative mTaskPersister.wakeup(null, true); } + // Sort by taskId + private Comparator<TaskRecord> mTaskRecordComparator = new Comparator<TaskRecord>() { + @Override + public int compare(TaskRecord lhs, TaskRecord rhs) { + return rhs.taskId - lhs.taskId; + } + }; + + // Extract the affiliates of the chain containing mRecentTasks[start]. + private int processNextAffiliateChain(int start) { + final TaskRecord startTask = mRecentTasks.get(start); + final int affiliateId = startTask.mAffiliatedTaskId; + + // Quick identification of isolated tasks. I.e. those not launched behind. + if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null && + startTask.mNextAffiliate == null) { + // There is still a slim chance that there are other tasks that point to this task + // and that the chain is so messed up that this task no longer points to them but + // the gain of this optimization outweighs the risk. + startTask.inRecents = true; + return start + 1; + } + + // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents. + mTmpRecents.clear(); + for (int i = mRecentTasks.size() - 1; i >= start; --i) { + final TaskRecord task = mRecentTasks.get(i); + if (task.mAffiliatedTaskId == affiliateId) { + mRecentTasks.remove(i); + mTmpRecents.add(task); + } + } + + // Sort them all by taskId. That is the order they were create in and that order will + // always be correct. + Collections.sort(mTmpRecents, mTaskRecordComparator); + + // Go through and fix up the linked list. + // The first one is the end of the chain and has no next. + final TaskRecord first = mTmpRecents.get(0); + first.inRecents = true; + if (first.mNextAffiliate != null) { + Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate); + first.setNextAffiliate(null); + mTaskPersister.wakeup(first, false); + } + // Everything in the middle is doubly linked from next to prev. + final int tmpSize = mTmpRecents.size(); + for (int i = 0; i < tmpSize - 1; ++i) { + final TaskRecord next = mTmpRecents.get(i); + final TaskRecord prev = mTmpRecents.get(i + 1); + if (next.mPrevAffiliate != prev) { + Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate + + " setting prev=" + prev); + next.setPrevAffiliate(prev); + mTaskPersister.wakeup(next, false); + } + if (prev.mNextAffiliate != next) { + Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate + + " setting next=" + next); + prev.setNextAffiliate(next); + mTaskPersister.wakeup(prev, false); + } + prev.inRecents = true; + } + // The last one is the beginning of the list and has no prev. + final TaskRecord last = mTmpRecents.get(tmpSize - 1); + if (last.mPrevAffiliate != null) { + Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate); + last.setPrevAffiliate(null); + mTaskPersister.wakeup(last, false); + } + + // Insert the group back into mRecentTasks at start. + mRecentTasks.addAll(start, mTmpRecents); + + // Let the caller know where we left off. + return start + tmpSize; + } + /** * Update the recent tasks lists: make sure tasks should still be here (their * applications / activities still exist), update their availability, fixup ordering @@ -3969,51 +4049,9 @@ public final class ActivityManagerService extends ActivityManagerNative } // Verify the affiliate chain for each task. - for (int i = 0; i < N; ) { - TaskRecord task = mRecentTasks.remove(i); - if (mTmpRecents.contains(task)) { - continue; - } - int affiliatedTaskId = task.mAffiliatedTaskId; - while (true) { - TaskRecord next = task.mNextAffiliate; - if (next == null) { - break; - } - if (next.mAffiliatedTaskId != affiliatedTaskId) { - Slog.e(TAG, "Error in Recents: next.affiliatedTaskId=" + - next.mAffiliatedTaskId + " affiliatedTaskId=" + affiliatedTaskId); - task.setNextAffiliate(null); - if (next.mPrevAffiliate == task) { - next.setPrevAffiliate(null); - } - break; - } - if (next.mPrevAffiliate != task) { - Slog.e(TAG, "Error in Recents chain prev.mNextAffiliate=" + - next.mPrevAffiliate + " task=" + task); - next.setPrevAffiliate(null); - task.setNextAffiliate(null); - break; - } - if (!mRecentTasks.contains(next)) { - Slog.e(TAG, "Error in Recents: next=" + next + " not in mRecentTasks"); - task.setNextAffiliate(null); - // We know that next.mPrevAffiliate is always task, from above, so clear - // its previous affiliate. - next.setPrevAffiliate(null); - break; - } - task = next; - } - // task is now the end of the list - do { - mRecentTasks.remove(task); - mRecentTasks.add(i++, task); - mTmpRecents.add(task); - task.inRecents = true; - } while ((task = task.mPrevAffiliate) != null); + for (int i = 0; i < N; i = processNextAffiliateChain(i)) { } + mTmpRecents.clear(); // mRecentTasks is now in sorted, affiliated order. } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index bba786d..957d705 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -43,12 +43,16 @@ public class NetworkAgentInfo { public Network network; public LinkProperties linkProperties; public NetworkCapabilities networkCapabilities; - public int currentScore; public final NetworkMonitor networkMonitor; public final NetworkMisc networkMisc; public boolean created; public boolean validated; + // This represents the last score received from the NetworkAgent. + private int currentScore; + // Penalty applied to scores of Networks that have not been validated. + private static final int UNVALIDATED_SCORE_PENALTY = 40; + // The list of NetworkRequests being satisfied by this Network. public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>(); public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>(); @@ -80,11 +84,33 @@ public class NetworkAgentInfo { return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN); } + // Get the current score for this Network. This may be modified from what the + // NetworkAgent sent, as it has modifiers applied to it. + public int getCurrentScore() { + // TODO: We may want to refactor this into a NetworkScore class that takes a base score from + // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the + // score. The NetworkScore class would provide a nice place to centralize score constants + // so they are not scattered about the transports. + + int score = currentScore; + + if (!validated) score -= UNVALIDATED_SCORE_PENALTY; + + if (score < 0) score = 0; + + return score; + } + + public void setCurrentScore(int newScore) { + currentScore = newScore; + } + public String toString() { return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" + network + "} lp{" + linkProperties + "} nc{" + - networkCapabilities + "} Score{" + currentScore + "} }"; + networkCapabilities + "} Score{" + getCurrentScore() + "} " + + "validated{" + validated + "} created{" + created + "} }"; } public String name() { diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index 96872a7..9e33205 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -95,6 +95,18 @@ public class NetworkMonitor extends StateMachine { "android.net.netmon.captive_portal_logged_in"; private static final String LOGGED_IN_RESULT = "result"; + // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. + // The network should be used as a default internet connection. It was found to be: + // 1. a functioning network providing internet access, or + // 2. a captive portal and the user decided to use it as is. + public static final int NETWORK_TEST_RESULT_VALID = 0; + // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. + // The network should not be used as a default internet connection. It was found to be: + // 1. a captive portal and the user is prompted to sign-in, or + // 2. a captive portal and the user did not want to use it, or + // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed). + public static final int NETWORK_TEST_RESULT_INVALID = 1; + private static final int BASE = Protocol.BASE_NETWORK_MONITOR; /** @@ -104,10 +116,11 @@ public class NetworkMonitor extends StateMachine { public static final int CMD_NETWORK_CONNECTED = BASE + 1; /** - * Inform ConnectivityService that the network is validated. + * Inform ConnectivityService that the network has been tested. * obj = NetworkAgentInfo + * arg1 = One of the NETWORK_TESTED_RESULT_* constants. */ - public static final int EVENT_NETWORK_VALIDATED = BASE + 2; + public static final int EVENT_NETWORK_TESTED = BASE + 2; /** * Inform NetworkMonitor to linger a network. The Monitor should @@ -216,6 +229,9 @@ public class NetworkMonitor extends StateMachine { private String mServer; private boolean mIsCaptivePortalCheckEnabled = false; + // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. + private boolean mUserDoesNotWant = false; + public boolean systemReady = false; private State mDefaultState = new DefaultState(); @@ -290,9 +306,23 @@ public class NetworkMonitor extends StateMachine { private class OfflineState extends State { @Override + public void enter() { + mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, + NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo)); + } + + @Override public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString()); - return NOT_HANDLED; + switch (message.what) { + case CMD_FORCE_REEVALUATION: + // If the user has indicated they explicitly do not want to use this network, + // don't allow a reevaluation as this will be pointless and could result in + // the user being annoyed with repeated unwanted notifications. + return mUserDoesNotWant ? HANDLED : NOT_HANDLED; + default: + return NOT_HANDLED; + } } } @@ -300,8 +330,8 @@ public class NetworkMonitor extends StateMachine { @Override public void enter() { if (DBG) log("Validated"); - mConnectivityServiceHandler.sendMessage( - obtainMessage(EVENT_NETWORK_VALIDATED, mNetworkAgentInfo)); + mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, + NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo)); } @Override @@ -393,6 +423,8 @@ public class NetworkMonitor extends StateMachine { @Override public void enter() { + mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, + NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo)); // Wait for user to select sign-in notifcation. mUserRespondedBroadcastReceiver = new UserRespondedBroadcastReceiver( ++mUserPromptedToken); @@ -477,6 +509,7 @@ public class NetworkMonitor extends StateMachine { if (message.arg1 != mCaptivePortalLoggedInToken) return HANDLED; if (message.arg2 == 0) { + mUserDoesNotWant = true; // TODO: Should teardown network. transitionTo(mOfflineState); } else { @@ -544,6 +577,12 @@ public class NetworkMonitor extends StateMachine { mConnectivityServiceHandler.sendMessage( obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo)); return HANDLED; + case CMD_FORCE_REEVALUATION: + // Ignore reevaluation attempts when lingering. A reevaluation could result + // in a transition to the validated state which would abort the linger + // timeout. Lingering is the result of score assessment; validity is + // irrelevant. + return HANDLED; default: return NOT_HANDLED; } diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index 11818d8..df846a8 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -494,7 +494,7 @@ public class GpsLocationProvider implements LocationProviderInterface { Log.d(TAG, "SIM STATE is ready, SIM MCC/MNC is " + mccMnc); synchronized (mLock) { reloadGpsProperties(context, mProperties); - mNIHandler.setSuplEsEnablement(mSuplEsEnabled); + mNIHandler.setSuplEsEnabled(mSuplEsEnabled); } } } @@ -590,6 +590,7 @@ public class GpsLocationProvider implements LocationProviderInterface { com.android.internal.R.array.config_gpsParameters); for (String item : configValues) { Log.d(TAG, "GpsParamsResource: " + item); + // We need to support "KEY =", but not "=VALUE". String[] split = item.split("="); if (split.length == 2) { properties.setProperty(split[0].trim().toUpperCase(), split[1]); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index efaf253..95332bc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3716,7 +3716,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (this) { // Only SYSTEM_UID can override the userSetupComplete if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID - && isUserSetupComplete(userHandle)) { + && hasUserSetupCompleted(userHandle)) { throw new IllegalStateException( "Trying to set profile owner but user is already set-up."); } @@ -3773,10 +3773,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean hasUserSetupCompleted() { + return hasUserSetupCompleted(UserHandle.getCallingUserId()); + } + + private boolean hasUserSetupCompleted(int userHandle) { if (!mHasFeature) { return true; } - DevicePolicyData policy = getUserData(UserHandle.getCallingUserId()); + DevicePolicyData policy = getUserData(userHandle); // If policy is null, return true, else check if the setup has completed. return policy == null || policy.mUserSetupComplete; } @@ -3888,16 +3892,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (callingId == Process.SHELL_UID || callingId == Process.ROOT_UID) { return AccountManager.get(mContext).getAccounts().length == 0; } else { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 0) == 0; + return !hasUserSetupCompleted(UserHandle.USER_OWNER); } } - private boolean isUserSetupComplete(int userId) { - return Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0, userId) > 0; - } - private void enforceCrossUserPermission(int userHandle) { if (userHandle < 0) { throw new IllegalArgumentException("Invalid userId " + userHandle); |