diff options
Diffstat (limited to 'services/java/com')
34 files changed, 2072 insertions, 2584 deletions
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 158c778..0b15221 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -1072,7 +1072,7 @@ class AppWidgetService extends IAppWidgetService.Stub throw new IllegalArgumentException("packageName and uid don't match packageName=" + packageName); } - if (callingUid != packageUid && Process.supportsProcesses()) { + if (callingUid != packageUid) { throw new IllegalArgumentException("packageName and uid don't match packageName=" + packageName); } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 3aa1239..786f2fa 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -23,6 +23,7 @@ import android.app.IActivityManager; import android.app.IApplicationThread; import android.app.IBackupAgent; import android.app.PendingIntent; +import android.app.backup.BackupAgent; import android.app.backup.BackupDataOutput; import android.app.backup.FullBackup; import android.app.backup.RestoreSet; @@ -64,6 +65,7 @@ import android.os.SystemClock; import android.os.WorkSource; import android.provider.Settings; import android.util.EventLog; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -222,6 +224,7 @@ class BackupManagerService extends IBackupManager.Stub { public PackageInfo pkgInfo; public int pmToken; // in post-install restore, the PM's token for this transaction public boolean needFullBackup; + public String[] filterSet; RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) { @@ -231,6 +234,7 @@ class BackupManagerService extends IBackupManager.Stub { pkgInfo = _pkg; pmToken = _pmToken; needFullBackup = _needFullBackup; + filterSet = null; } RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, @@ -241,6 +245,18 @@ class BackupManagerService extends IBackupManager.Stub { pkgInfo = null; pmToken = 0; needFullBackup = _needFullBackup; + filterSet = null; + } + + RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, + String[] _filterSet, boolean _needFullBackup) { + transport = _transport; + observer = _obs; + token = _token; + pkgInfo = null; + pmToken = 0; + needFullBackup = _needFullBackup; + filterSet = _filterSet; } } @@ -402,7 +418,7 @@ class BackupManagerService extends IBackupManager.Stub { Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); (new PerformRestoreTask(params.transport, params.observer, params.token, params.pkgInfo, params.pmToken, - params.needFullBackup)).run(); + params.needFullBackup, params.filterSet)).run(); break; } @@ -1587,8 +1603,7 @@ class BackupManagerService extends IBackupManager.Stub { // Initiate the target's backup pass prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL); - agent.doBackup(savedState, backupData, newState, false, - token, mBackupManagerBinder); + agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder); boolean success = waitUntilOperationComplete(token); if (!success) { @@ -1764,30 +1779,31 @@ class BackupManagerService extends IBackupManager.Stub { if (agent != null) { try { ApplicationInfo app = pkg.applicationInfo; - boolean sendApk = mIncludeApks + final boolean sendApk = mIncludeApks && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0) && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 || (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); sendOnBackupPackage(pkg.packageName); - { - BackupDataOutput output = new BackupDataOutput( - mOutputFile.getFileDescriptor()); + BackupDataOutput output = new BackupDataOutput( + mOutputFile.getFileDescriptor()); - if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName); - writeAppManifest(pkg, mManifestFile, sendApk); - FullBackup.backupToTar(pkg.packageName, null, null, - mFilesDir.getAbsolutePath(), - mManifestFile.getAbsolutePath(), - output); + if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName); + writeAppManifest(pkg, mManifestFile, sendApk); + FullBackup.backupToTar(pkg.packageName, null, null, + mFilesDir.getAbsolutePath(), + mManifestFile.getAbsolutePath(), + output); + + if (sendApk) { + writeApkToBackup(pkg, output); } - if (DEBUG) Slog.d(TAG, "Calling doBackup()"); + if (DEBUG) Slog.d(TAG, "Calling doFullBackup()"); final int token = generateToken(); prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL); - agent.doBackup(null, mOutputFile, null, sendApk, - token, mBackupManagerBinder); + agent.doFullBackup(mOutputFile, token, mBackupManagerBinder); if (!waitUntilOperationComplete(token)) { Slog.e(TAG, "Full backup failed on package " + pkg.packageName); } else { @@ -1802,6 +1818,29 @@ class BackupManagerService extends IBackupManager.Stub { tearDown(pkg); } + private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) { + // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here + final String appSourceDir = pkg.applicationInfo.sourceDir; + final String apkDir = new File(appSourceDir).getParent(); + FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null, + apkDir, appSourceDir, output); + + // Save associated .obb content if it exists and we did save the apk + // check for .obb and save those too + final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName); + if (obbDir != null) { + if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); + File[] obbFiles = obbDir.listFiles(); + if (obbFiles != null) { + final String obbDirName = obbDir.getAbsolutePath(); + for (File obb : obbFiles) { + FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null, + obbDirName, obb.getAbsolutePath(), output); + } + } + } + } + private void backupSharedStorage() throws RemoteException { PackageInfo pkg = null; try { @@ -1813,7 +1852,7 @@ class BackupManagerService extends IBackupManager.Stub { final int token = generateToken(); prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL); - agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder); + agent.doFullBackup(mOutputFile, token, mBackupManagerBinder); if (!waitUntilOperationComplete(token)) { Slog.e(TAG, "Full backup failed on shared storage"); } else { @@ -1933,7 +1972,7 @@ class BackupManagerService extends IBackupManager.Stub { static class FileMetadata { String packageName; // name of the owning app String installerPackageName; // name of the market-type app that installed the owner - int type; // e.g. FullBackup.TYPE_DIRECTORY + int type; // e.g. BackupAgent.TYPE_DIRECTORY String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN String path; // subpath within the semantic domain long mode; // e.g. 0666 (actually int) @@ -2182,15 +2221,15 @@ class BackupManagerService extends IBackupManager.Stub { // If we haven't sent any data to this app yet, we probably // need to clear it first. Check that. if (!mClearedPackages.contains(pkg)) { - // apps with their own full backup agents are + // apps with their own backup agents are // responsible for coherently managing a full // restore. - if (mTargetApp.fullBackupAgentName == null) { + if (mTargetApp.backupAgentName == null) { if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); clearApplicationDataSynchronous(pkg); } else { - if (DEBUG) Slog.d(TAG, "full backup agent (" - + mTargetApp.fullBackupAgentName + ") => no clear"); + if (DEBUG) Slog.d(TAG, "backup agent (" + + mTargetApp.backupAgentName + ") => no clear"); } mClearedPackages.add(pkg); } else { @@ -2686,7 +2725,7 @@ class BackupManagerService extends IBackupManager.Stub { StringBuilder b = new StringBuilder(128); // mode string - b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-'); + b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); b.append(((info.mode & 0400) != 0) ? 'r' : '-'); b.append(((info.mode & 0200) != 0) ? 'w' : '-'); b.append(((info.mode & 0100) != 0) ? 'x' : '-'); @@ -2746,9 +2785,9 @@ class BackupManagerService extends IBackupManager.Stub { } switch (typeChar) { - case '0': info.type = FullBackup.TYPE_FILE; break; + case '0': info.type = BackupAgent.TYPE_FILE; break; case '5': { - info.type = FullBackup.TYPE_DIRECTORY; + info.type = BackupAgent.TYPE_DIRECTORY; if (info.size != 0) { Slog.w(TAG, "Directory entry with nonzero size in header"); info.size = 0; @@ -2995,6 +3034,7 @@ class BackupManagerService extends IBackupManager.Stub { private File mStateDir; private int mPmToken; private boolean mNeedFullBackup; + private HashSet<String> mFilterSet; class RestoreRequest { public PackageInfo app; @@ -3008,7 +3048,7 @@ class BackupManagerService extends IBackupManager.Stub { PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer, long restoreSetToken, PackageInfo targetPackage, int pmToken, - boolean needFullBackup) { + boolean needFullBackup, String[] filterSet) { mTransport = transport; mObserver = observer; mToken = restoreSetToken; @@ -3016,6 +3056,15 @@ class BackupManagerService extends IBackupManager.Stub { mPmToken = pmToken; mNeedFullBackup = needFullBackup; + if (filterSet != null) { + mFilterSet = new HashSet<String>(); + for (String pkg : filterSet) { + mFilterSet.add(pkg); + } + } else { + mFilterSet = null; + } + try { mStateDir = new File(mBaseStateDir, transport.transportDirName()); } catch (RemoteException e) { @@ -3027,7 +3076,8 @@ class BackupManagerService extends IBackupManager.Stub { long startRealtime = SystemClock.elapsedRealtime(); if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken) - + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken); + + " mTargetPackage=" + mTargetPackage + " mFilterSet=" + mFilterSet + + " mPmToken=" + mPmToken); PackageManagerBackupAgent pmAgent = null; int error = -1; // assume error @@ -3046,6 +3096,22 @@ class BackupManagerService extends IBackupManager.Stub { List<PackageInfo> agentPackages = allAgentPackages(); if (mTargetPackage == null) { + // if there's a filter set, strip out anything that isn't + // present before proceeding + if (mFilterSet != null) { + for (int i = agentPackages.size() - 1; i >= 0; i--) { + final PackageInfo pkg = agentPackages.get(i); + if (! mFilterSet.contains(pkg.packageName)) { + agentPackages.remove(i); + } + } + if (DEBUG) { + Slog.i(TAG, "Post-filter package set for restore:"); + for (PackageInfo p : agentPackages) { + Slog.i(TAG, " " + p); + } + } + } restorePackages.addAll(agentPackages); } else { // Just one package to attempt restore of @@ -4241,6 +4307,67 @@ class BackupManagerService extends IBackupManager.Stub { return -1; } + public synchronized int restoreSome(long token, IRestoreObserver observer, + String[] packages) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, + "performRestore"); + + if (DEBUG) { + StringBuilder b = new StringBuilder(128); + b.append("restoreSome token="); + b.append(Long.toHexString(token)); + b.append(" observer="); + b.append(observer.toString()); + b.append(" packages="); + if (packages == null) { + b.append("null"); + } else { + b.append('{'); + boolean first = true; + for (String s : packages) { + if (!first) { + b.append(", "); + } else first = false; + b.append(s); + } + b.append('}'); + } + Slog.d(TAG, b.toString()); + } + + if (mEnded) { + throw new IllegalStateException("Restore session already ended"); + } + + if (mRestoreTransport == null || mRestoreSets == null) { + Slog.e(TAG, "Ignoring restoreAll() with no restore set"); + return -1; + } + + if (mPackageName != null) { + Slog.e(TAG, "Ignoring restoreAll() on single-package session"); + return -1; + } + + synchronized (mQueueLock) { + for (int i = 0; i < mRestoreSets.length; i++) { + if (token == mRestoreSets[i].token) { + long oldId = Binder.clearCallingIdentity(); + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(mRestoreTransport, observer, token, + packages, true); + mBackupHandler.sendMessage(msg); + Binder.restoreCallingIdentity(oldId); + return 0; + } + } + } + + Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); + return -1; + } + public synchronized int restorePackage(String packageName, IRestoreObserver observer) { if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index e6f443a..41450d2 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -44,7 +44,6 @@ import android.net.NetworkUtils; import android.net.Proxy; import android.net.ProxyProperties; import android.net.RouteInfo; -import android.net.vpn.VpnManager; import android.net.wifi.WifiStateTracker; import android.os.Binder; import android.os.FileUtils; @@ -65,6 +64,7 @@ import android.util.EventLog; import android.util.Slog; import android.util.SparseIntArray; +import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.telephony.Phone; import com.android.server.connectivity.Tethering; @@ -131,8 +131,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private List mNetRequestersPids[]; - private WifiWatchdogService mWifiWatchdogService; - // priority order of the nettrackers // (excluding dynamically set mNetworkPreference) // TODO - move mNetworkTypePreference into this @@ -278,6 +276,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } RadioAttributes[] mRadioAttributes; + // the set of network types that can only be enabled by system/sig apps + List mProtectedNetworks; + public ConnectivityService( Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) { if (DBG) log("ConnectivityService starting up"); @@ -381,6 +382,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + mProtectedNetworks = new ArrayList<Integer>(); + int[] protectedNetworks = context.getResources().getIntArray( + com.android.internal.R.array.config_protectedNetworks); + for (int p : protectedNetworks) { + if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) { + mProtectedNetworks.add(p); + } else { + if (DBG) loge("Ignoring protectedNetwork " + p); + } + } + // high priority first mPriorityList = new int[mNetworksDefined]; { @@ -432,10 +444,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { wifiService.checkAndStartWifi(); mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; wst.startMonitoring(context, mHandler); - - //TODO: as part of WWS refactor, create only when needed - mWifiWatchdogService = new WifiWatchdogService(context); - break; case ConnectivityManager.TYPE_MOBILE: mNetTrackers[netType] = new MobileDataStateTracker(netType, @@ -488,11 +496,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { mSettingsObserver.observe(mContext); loadGlobalProxy(); - - VpnManager.startVpnService(context); } - /** * Sets the preferred network. * @param preference the new preference @@ -802,6 +807,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { usedNetworkType = networkType; } } + + if (mProtectedNetworks.contains(usedNetworkType)) { + enforceConnectivityInternalPermission(); + } + NetworkStateTracker network = mNetTrackers[usedNetworkType]; if (network != null) { Integer currentPid = new Integer(getCallingPid()); @@ -1012,6 +1022,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) { enforceChangePermission(); + if (mProtectedNetworks.contains(networkType)) { + enforceConnectivityInternalPermission(); + } + if (!ConnectivityManager.isNetworkTypeValid(networkType)) { return false; } @@ -1129,7 +1143,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } public void setDataDependency(int networkType, boolean met) { - enforceChangePermission(); + enforceConnectivityInternalPermission(); + if (DBG) { log("setDataDependency(" + networkType + ", " + met + ")"); } @@ -1587,11 +1602,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (linkProperties != null) { String iface = linkProperties.getInterfaceName(); if (TextUtils.isEmpty(iface) == false) { - if (DBG) log("resetConnections(" + iface + ")"); - NetworkUtils.resetConnections(iface); + if (DBG) { + log("resetConnections(" + iface + ", NetworkUtils.RESET_ALL_ADDRESSES)"); + } + NetworkUtils.resetConnections(iface, NetworkUtils.RESET_ALL_ADDRESSES); } } } + + // TODO: Temporary notifying upstread change to Tethering. + // @see bug/4455071 + /** Notify TetheringService if interface name has been changed. */ + if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(), + Phone.REASON_LINK_PROPERTIES_CHANGED)) { + if (isTetheringSupported()) { + mTethering.handleTetherIfaceChange(); + } + } } private void addPrivateDnsRoutes(NetworkStateTracker nt) { @@ -2017,7 +2044,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { break; case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: info = (NetworkInfo) msg.obj; - handleConnectivityChange(info.getType(), true); + // TODO: Temporary allowing network configuration + // change not resetting sockets. + // @see bug/4455071 + handleConnectivityChange(info.getType(), false); break; case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: String causedBy = null; @@ -2442,8 +2472,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { /** * Protect a socket from VPN routing rules. This method is used by - * VpnBuilder and not available in ConnectivityManager. Permission - * checks are done in Vpn class. + * VpnBuilder and not available in ConnectivityManager. Permissions + * are checked in Vpn class. * @hide */ @Override @@ -2453,20 +2483,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { /** * Prepare for a VPN application. This method is used by VpnDialogs - * and not available in ConnectivityManager. Permission checks are - * done in Vpn class. + * and not available in ConnectivityManager. Permissions are checked + * in Vpn class. * @hide */ @Override - public String prepareVpn(String packageName) { - return mVpn.prepare(packageName); + public boolean prepareVpn(String oldPackage, String newPackage) { + return mVpn.prepare(oldPackage, newPackage); } /** * Configure a TUN interface and return its file descriptor. Parameters * are encoded and opaque to this class. This method is used by VpnBuilder - * and not available in ConnectivityManager. Permission checks are done - * in Vpn class. + * and not available in ConnectivityManager. Permissions are checked in + * Vpn class. * @hide */ @Override @@ -2474,6 +2504,28 @@ public class ConnectivityService extends IConnectivityManager.Stub { return mVpn.establish(config); } + /** + * Start legacy VPN and return an intent to VpnDialogs. This method is + * used by VpnSettings and not available in ConnectivityManager. + * Permissions are checked in Vpn class. + * @hide + */ + @Override + public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { + mVpn.startLegacyVpn(config, racoon, mtpd); + } + + /** + * Return the information of the ongoing legacy VPN. This method is used + * by VpnSettings and not available in ConnectivityManager. Permissions + * are checked in Vpn class. + * @hide + */ + @Override + public LegacyVpnInfo getLegacyVpnInfo() { + return mVpn.getLegacyVpnInfo(); + } + private String getDefaultInterface() { if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) { NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork]; @@ -2502,7 +2554,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private VpnCallback() { } - public synchronized void override(String[] dnsServers) { + public synchronized void override(List<String> dnsServers, List<String> searchDomains) { // TODO: override DNS servers and http proxy. } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 8037d7a..2d55433 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -137,6 +137,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID); private static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; private static final String SUBTYPE_MODE_VOICE = "voice"; + private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; final Context mContext; final Resources mRes; @@ -1057,25 +1058,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub synchronized (mMethodMap) { List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked(); final int N = imis.size(); - int count = 0; + if (N > 2) return true; + if (N < 1) return false; + int nonAuxCount = 0; + int auxCount = 0; + InputMethodSubtype nonAuxSubtype = null; + InputMethodSubtype auxSubtype = null; for(int i = 0; i < N; ++i) { final InputMethodInfo imi = imis.get(i); final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeListLocked( imi, true); final int subtypeCount = subtypes.size(); if (subtypeCount == 0) { - ++count; + ++nonAuxCount; } else { for (int j = 0; j < subtypeCount; ++j) { - if (!subtypes.get(j).isAuxiliary()) { - ++count; + final InputMethodSubtype subtype = subtypes.get(j); + if (!subtype.isAuxiliary()) { + ++nonAuxCount; + nonAuxSubtype = subtype; + } else { + ++auxCount; + auxSubtype = subtype; } } } - if (count > 1) return true; } + if (nonAuxCount > 1 || auxCount > 1) { + return true; + } else if (nonAuxCount == 1 && auxCount == 1) { + if (nonAuxSubtype != null && auxSubtype != null + && nonAuxSubtype.getLocale().equals(auxSubtype.getLocale()) + && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) { + return false; + } + return true; + } + return false; } - return false; } @Override @@ -1603,8 +1623,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (lastImi == null) return null; try { final int lastSubtypeHash = Integer.valueOf(lastIme.second); - return lastImi.getSubtypeAt(getSubtypeIdFromHashCode( - lastImi, lastSubtypeHash)); + final int lastSubtypeId = getSubtypeIdFromHashCode(lastImi, lastSubtypeHash); + if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) { + return null; + } + return lastImi.getSubtypeAt(lastSubtypeId); } catch (NumberFormatException e) { return null; } @@ -1621,7 +1644,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi == null) return false; final int N = subtypes.length; - mFileManager.addInputMethodSubtypes(mCurMethodId, subtypes); + mFileManager.addInputMethodSubtypes(imi, subtypes); buildInputMethodListLocked(mMethodList, mMethodMap); return true; } @@ -2003,25 +2026,26 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final CharSequence label = imi.loadLabel(pm); if (showSubtypes && enabledSubtypeSet.size() > 0) { final int subtypeCount = imi.getSubtypeCount(); + if (DEBUG) { + Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); + } for (int j = 0; j < subtypeCount; ++j) { - InputMethodSubtype subtype = imi.getSubtypeAt(j); - if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode())) - && !subtype.isAuxiliary()) { + final InputMethodSubtype subtype = imi.getSubtypeAt(j); + final String subtypeHashCode = String.valueOf(subtype.hashCode()); + // We show all enabled IMEs and subtypes when an IME is shown. + if (enabledSubtypeSet.contains(subtypeHashCode) + && (mInputShown || !subtype.isAuxiliary())) { final CharSequence title; - int nameResId = subtype.getNameResId(); - String mode = subtype.getMode(); - if (nameResId != 0) { - title = TextUtils.concat(subtype.getDisplayName(context, - imi.getPackageName(), imi.getServiceInfo().applicationInfo), - (TextUtils.isEmpty(label) ? "" : " (" + label + ")")); - } else { - CharSequence language = subtype.getLocale(); - // TODO: Use more friendly Title and UI - title = label + "," + (mode == null ? "" : mode) + "," - + (language == null ? "" : language); - } + final String mode = subtype.getMode(); + title = TextUtils.concat(subtype.getDisplayName(context, + imi.getPackageName(), imi.getServiceInfo().applicationInfo), + (TextUtils.isEmpty(label) ? "" : " (" + label + ")")); imList.add(new Pair<CharSequence, Pair<InputMethodInfo, Integer>>( title, new Pair<InputMethodInfo, Integer>(imi, j))); + // Removing this subtype from enabledSubtypeSet because we no longer + // need to add an entry of this subtype to imList to avoid duplicated + // entries. + enabledSubtypeSet.remove(subtypeHashCode); } } } else { @@ -2318,7 +2342,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( + final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( applicableModeAndSubtypesMap.values()); if (!containsKeyboardSubtype) { InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked( @@ -2996,17 +3020,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void addInputMethodSubtypes( - String imiId, InputMethodSubtype[] additionalSubtypes) { + InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) { synchronized (mMethodMap) { + final HashSet<InputMethodSubtype> existingSubtypes = + new HashSet<InputMethodSubtype>(); + for (int i = 0; i < imi.getSubtypeCount(); ++i) { + existingSubtypes.add(imi.getSubtypeAt(i)); + } + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); final int N = additionalSubtypes.length; for (int i = 0; i < N; ++i) { final InputMethodSubtype subtype = additionalSubtypes[i]; - if (!subtypes.contains(subtype)) { + if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) { subtypes.add(subtype); } } - mSubtypesMap.put(imiId, subtypes); + mSubtypesMap.put(imi.getId(), subtypes); writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap); } @@ -3113,8 +3143,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE); final String imeSubtypeExtraValue = parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE); - final boolean isAuxiliary = - Boolean.valueOf(parser.getAttributeValue(null, ATTR_IS_AUXILIARY)); + final boolean isAuxiliary = "1".equals(String.valueOf( + parser.getAttributeValue(null, ATTR_IS_AUXILIARY))); final InputMethodSubtype subtype = new InputMethodSubtype(label, icon, imeSubtypeLocale, imeSubtypeMode, imeSubtypeExtraValue, isAuxiliary); diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index 1d3e3ac..b3d7220 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -41,7 +41,7 @@ import android.content.IntentFilter; /** * {@hide} */ -public class IntentResolver<F extends IntentFilter, R extends Object> { +public abstract class IntentResolver<F extends IntentFilter, R extends Object> { final private static String TAG = "IntentResolver"; final private static boolean DEBUG = false; final private static boolean localLOGV = DEBUG || false; @@ -333,14 +333,19 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return false; } - protected String packageForFilter(F filter) { - return null; - } + /** + * Return the package that owns this filter. This must be implemented to + * provide correct filtering of Intents that have specified a package name + * they are to be delivered to. + */ + protected abstract String packageForFilter(F filter); + @SuppressWarnings("unchecked") protected R newResult(F filter, int match) { return (R)filter; } + @SuppressWarnings("unchecked") protected void sortResults(List<R> results) { Collections.sort(results, mResolvePrioritySorter); } @@ -502,6 +507,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { String resolvedType, String scheme, List<F> src, List<R> dest) { final String action = intent.getAction(); final Uri data = intent.getData(); + final String packageName = intent.getPackage(); final boolean excludingStopped = intent.isExcludingStopped(); @@ -520,6 +526,14 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { continue; } + // Is delivery being limited to filters owned by a particular package? + if (packageName != null && !packageName.equals(packageForFilter(filter))) { + if (debug) { + Slog.v(TAG, " Filter is not from package " + packageName + "; skipping"); + } + continue; + } + // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { @@ -561,6 +575,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { } // Sorts a List of IntentFilter objects into descending priority order. + @SuppressWarnings("rawtypes") private static final Comparator mResolvePrioritySorter = new Comparator() { public int compare(Object o1, Object o2) { final int q1 = ((IntentFilter) o1).getPriority(); diff --git a/services/java/com/android/server/LoadAverageService.java b/services/java/com/android/server/LoadAverageService.java index b6baadb..da9fc99 100644 --- a/services/java/com/android/server/LoadAverageService.java +++ b/services/java/com/android/server/LoadAverageService.java @@ -278,14 +278,14 @@ public class LoadAverageService extends Service { PixelFormat.TRANSLUCENT); params.gravity = Gravity.RIGHT | Gravity.TOP; params.setTitle("Load Average"); - WindowManagerImpl wm = (WindowManagerImpl)getSystemService(WINDOW_SERVICE); + WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE); wm.addView(mView, params); } @Override public void onDestroy() { super.onDestroy(); - ((WindowManagerImpl)getSystemService(WINDOW_SERVICE)).removeView(mView); + ((WindowManager)getSystemService(WINDOW_SERVICE)).removeView(mView); mView = null; } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 656ec4d..56afe7f 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -195,6 +195,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final Object mKey; final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>(); int mPendingBroadcasts; + String requiredPermissions; Receiver(ILocationListener listener) { mListener = listener; @@ -284,7 +285,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler); + mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -319,7 +321,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler); + mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -358,7 +361,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler); + mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -572,22 +576,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return Settings.Secure.isLocationProviderEnabled(resolver, provider); } - private void checkPermissionsSafe(String provider) { - if ((LocationManager.GPS_PROVIDER.equals(provider) - || LocationManager.PASSIVE_PROVIDER.equals(provider)) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION permission"); + private String checkPermissionsSafe(String provider, String lastPermission) { + if (LocationManager.GPS_PROVIDER.equals(provider) + || LocationManager.PASSIVE_PROVIDER.equals(provider)) { + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Provider " + provider + + " requires ACCESS_FINE_LOCATION permission"); + } + return ACCESS_FINE_LOCATION; } - if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); + + // Assume any other provider requires the coarse or fine permission. + if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + return ACCESS_FINE_LOCATION.equals(lastPermission) + ? lastPermission : ACCESS_COARSE_LOCATION; } + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + return ACCESS_FINE_LOCATION; + } + + throw new SecurityException("Provider " + provider + + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); } private boolean isAllowedProviderSafe(String provider) { @@ -1099,8 +1111,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + void validatePendingIntent(PendingIntent intent) { + if (intent.isTargetedToPackage()) { + return; + } + Slog.i(TAG, "Given Intent does not require a specific package: " + + intent); + // XXX we should really throw a security exception, if the caller's + // targetSdkVersion is high enough. + //throw new SecurityException("Given Intent does not require a specific package: " + // + intent); + } + public void requestLocationUpdatesPI(String provider, Criteria criteria, long minTime, float minDistance, boolean singleShot, PendingIntent intent) { + validatePendingIntent(intent); if (criteria != null) { // FIXME - should we consider using multiple providers simultaneously // rather than only the best one? @@ -1132,7 +1157,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run throw new IllegalArgumentException("provider=" + provider); } - checkPermissionsSafe(provider); + receiver.requiredPermissions = checkPermissionsSafe(provider, + receiver.requiredPermissions); // so wakelock calls will succeed final int callingUid = Binder.getCallingUid(); @@ -1300,7 +1326,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } // first check for permission to the provider - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); // and check for ACCESS_LOCATION_EXTRA_COMMANDS if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { @@ -1432,7 +1458,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcasts() // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, enteredIntent, this, mLocationHandler); + intent.send(mContext, 0, enteredIntent, this, mLocationHandler, + ACCESS_FINE_LOCATION); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcasts(); @@ -1457,7 +1484,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcasts() // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, exitedIntent, this, mLocationHandler); + intent.send(mContext, 0, exitedIntent, this, mLocationHandler, + ACCESS_FINE_LOCATION); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcasts(); @@ -1526,6 +1554,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public void addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) { + validatePendingIntent(intent); try { synchronized (mLock) { addProximityAlertLocked(latitude, longitude, radius, expiration, intent); @@ -1626,7 +1655,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return null; } - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); Bundle b = new Bundle(); b.putBoolean("network", p.requiresNetwork()); @@ -1668,7 +1697,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private boolean _isProviderEnabledLocked(String provider) { - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { @@ -1694,7 +1723,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private Location _getLastKnownLocationLocked(String provider) { - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index d3244ec..2e54c99 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1075,18 +1075,22 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC com.android.internal.R.styleable.Storage_mtpReserve, 0); boolean allowMassStorage = a.getBoolean( com.android.internal.R.styleable.Storage_allowMassStorage, false); + // resource parser does not support longs, so XML value is in megabytes + long maxFileSize = a.getInt( + com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L; Slog.d(TAG, "got storage path: " + path + " description: " + description + " primary: " + primary + " removable: " + removable + " emulated: " + emulated + " mtpReserve: " + mtpReserve + - " allowMassStorage: " + allowMassStorage); + " allowMassStorage: " + allowMassStorage + + " maxFileSize: " + maxFileSize); if (path == null || description == null) { Slog.e(TAG, "path or description is null in readStorageList"); } else { String pathString = path.toString(); StorageVolume volume = new StorageVolume(pathString, description.toString(), removable, emulated, - mtpReserve, allowMassStorage); + mtpReserve, allowMassStorage, maxFileSize); if (primary) { if (mPrimaryVolume == null) { mPrimaryVolume = volume; @@ -1626,6 +1630,30 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC } } + public String getSecureContainerFilesystemPath(String id) { + validatePermission(android.Manifest.permission.ASEC_ACCESS); + waitForReady(); + warnOnNotMounted(); + + try { + ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id)); + String []tok = rsp.get(0).split(" "); + int code = Integer.parseInt(tok[0]); + if (code != VoldResponseCode.AsecPathResult) { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + return tok[1]; + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageNotFound) { + Slog.i(TAG, String.format("Container '%s' not found", id)); + return null; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + } + public void finishMediaUpdate() { mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 2b01c5e..da1bf83 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -16,10 +16,11 @@ package com.android.server; +import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.Manifest.permission.MANAGE_NETWORK_POLICY; +import static android.provider.Settings.Secure.NETSTATS_ENABLED; import android.content.Context; import android.content.pm.PackageManager; @@ -35,8 +36,14 @@ import android.os.Binder; import android.os.INetworkManagementService; import android.os.SystemClock; import android.os.SystemProperties; +import android.provider.Settings; import android.util.Log; import android.util.Slog; +import android.util.SparseBooleanArray; + +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; +import com.google.android.collect.Sets; import java.io.BufferedReader; import java.io.DataInputStream; @@ -47,7 +54,10 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.Inet4Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.concurrent.CountDownLatch; @@ -65,9 +75,18 @@ class NetworkManagementService extends INetworkManagementService.Stub { private static final int ADD = 1; private static final int REMOVE = 2; + /** Path to {@code /proc/uid_stat}. */ @Deprecated - private static final File STATS_UIDSTAT = new File("/proc/uid_stat"); - private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats"); + private final File mProcStatsUidstat; + /** Path to {@code /proc/net/xt_qtaguid/stats}. */ + private final File mProcStatsNetfilter; + + /** {@link #mProcStatsNetfilter} headers. */ + private static final String KEY_IFACE = "iface"; + private static final String KEY_TAG_HEX = "acct_tag_hex"; + private static final String KEY_UID = "uid_tag_int"; + private static final String KEY_RX = "rx_bytes"; + private static final String KEY_TX = "tx_bytes"; class NetdResponseCode { public static final int InterfaceListResult = 110; @@ -102,15 +121,25 @@ class NetworkManagementService extends INetworkManagementService.Stub { private ArrayList<INetworkManagementEventObserver> mObservers; + /** Set of interfaces with active quotas. */ + private HashSet<String> mInterfaceQuota = Sets.newHashSet(); + /** Set of UIDs with active reject rules. */ + private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray(); + + private boolean mBandwidthControlEnabled; + /** * Constructs a new NetworkManagementService instance * * @param context Binder context for this service */ - private NetworkManagementService(Context context) { + private NetworkManagementService(Context context, File procRoot) { mContext = context; mObservers = new ArrayList<INetworkManagementEventObserver>(); + mProcStatsUidstat = new File(procRoot, "uid_stat"); + mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats"); + if ("simulator".equals(SystemProperties.get("ro.product.device"))) { return; } @@ -121,7 +150,8 @@ class NetworkManagementService extends INetworkManagementService.Stub { } public static NetworkManagementService create(Context context) throws InterruptedException { - NetworkManagementService service = new NetworkManagementService(context); + NetworkManagementService service = new NetworkManagementService( + context, new File("/proc/")); if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); service.mThread.start(); if (DBG) Slog.d(TAG, "Awaiting socket connection"); @@ -130,6 +160,35 @@ class NetworkManagementService extends INetworkManagementService.Stub { return service; } + // @VisibleForTesting + public static NetworkManagementService createForTest(Context context, File procRoot) { + // TODO: eventually connect with mock netd + return new NetworkManagementService(context, procRoot); + } + + public void systemReady() { + + // only enable bandwidth control when support exists, and requested by + // system setting. + // TODO: eventually migrate to be always enabled + final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists(); + final boolean shouldEnable = + Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 0) != 0; + + mBandwidthControlEnabled = false; + if (hasKernelSupport && shouldEnable) { + Slog.d(TAG, "enabling bandwidth control"); + try { + mConnector.doCommand("bandwidth enable"); + mBandwidthControlEnabled = true; + } catch (NativeDaemonConnectorException e) { + Slog.e(TAG, "problem enabling bandwidth controls", e); + } + } else { + Slog.d(TAG, "not enabling bandwidth control"); + } + } + public void registerObserver(INetworkManagementEventObserver obs) { Slog.d(TAG, "Registering observer"); mObservers.add(obs); @@ -141,12 +200,26 @@ class NetworkManagementService extends INetworkManagementService.Stub { } /** - * Notify our observers of an interface link status change + * Notify our observers of an interface status change + */ + private void notifyInterfaceStatusChanged(String iface, boolean up) { + for (INetworkManagementEventObserver obs : mObservers) { + try { + obs.interfaceStatusChanged(iface, up); + } catch (Exception ex) { + Slog.w(TAG, "Observer notifier failed", ex); + } + } + } + + /** + * Notify our observers of an interface link state change + * (typically, an Ethernet cable has been plugged-in or unplugged). */ - private void notifyInterfaceLinkStatusChanged(String iface, boolean link) { + private void notifyInterfaceLinkStateChanged(String iface, boolean up) { for (INetworkManagementEventObserver obs : mObservers) { try { - obs.interfaceLinkStatusChanged(iface, link); + obs.interfaceLinkStateChanged(iface, up); } catch (Exception ex) { Slog.w(TAG, "Observer notifier failed", ex); } @@ -207,6 +280,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { * Format: "NNN Iface added <name>" * "NNN Iface removed <name>" * "NNN Iface changed <name> <up/down>" + * "NNN Iface linkstatus <name> <up/down>" */ if (cooked.length < 4 || !cooked[1].equals("Iface")) { throw new IllegalStateException( @@ -219,7 +293,10 @@ class NetworkManagementService extends INetworkManagementService.Stub { notifyInterfaceRemoved(cooked[3]); return true; } else if (cooked[2].equals("changed") && cooked.length == 5) { - notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up")); + notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up")); + return true; + } else if (cooked[2].equals("linkstate") && cooked.length == 5) { + notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up")); return true; } throw new IllegalStateException( @@ -870,7 +947,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - if (STATS_NETFILTER.exists()) { + if (mBandwidthControlEnabled) { return getNetworkStatsDetailNetfilter(UID_ALL); } else { return getNetworkStatsDetailUidstat(UID_ALL); @@ -878,13 +955,103 @@ class NetworkManagementService extends INetworkManagementService.Stub { } @Override + public void setInterfaceQuota(String iface, long quota) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + synchronized (mInterfaceQuota) { + if (mInterfaceQuota.contains(iface)) { + // TODO: eventually consider throwing + return; + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth setiquota ").append(iface).append(" ").append(quota); + + try { + // TODO: add support for quota shared across interfaces + mConnector.doCommand(command.toString()); + mInterfaceQuota.add(iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + + @Override + public void removeInterfaceQuota(String iface) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + synchronized (mInterfaceQuota) { + if (!mInterfaceQuota.contains(iface)) { + // TODO: eventually consider throwing + return; + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth removeiquota ").append(iface); + + try { + // TODO: add support for quota shared across interfaces + mConnector.doCommand(command.toString()); + mInterfaceQuota.remove(iface); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + + @Override + public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + // silently discard when control disabled + // TODO: eventually migrate to be always enabled + if (!mBandwidthControlEnabled) return; + + synchronized (mUidRejectOnQuota) { + final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false); + if (oldRejectOnQuota == rejectOnQuotaInterfaces) { + // TODO: eventually consider throwing + return; + } + + final StringBuilder command = new StringBuilder(); + command.append("bandwidth"); + if (rejectOnQuotaInterfaces) { + command.append(" addnaughtyapps"); + } else { + command.append(" removenaughtyapps"); + } + command.append(" ").append(uid); + + try { + mConnector.doCommand(command.toString()); + if (rejectOnQuotaInterfaces) { + mUidRejectOnQuota.put(uid, true); + } else { + mUidRejectOnQuota.delete(uid); + } + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException("Error communicating to native daemon", e); + } + } + } + public NetworkStats getNetworkStatsUidDetail(int uid) { if (Binder.getCallingUid() != uid) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); } - if (STATS_NETFILTER.exists()) { + if (mBandwidthControlEnabled) { return getNetworkStatsDetailNetfilter(uid); } else { return getNetworkStatsDetailUidstat(uid); @@ -896,35 +1063,36 @@ class NetworkManagementService extends INetworkManagementService.Stub { */ private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); + final ArrayList<String> keys = Lists.newArrayList(); + final ArrayList<String> values = Lists.newArrayList(); + final HashMap<String, String> parsed = Maps.newHashMap(); BufferedReader reader = null; try { - reader = new BufferedReader(new FileReader(STATS_NETFILTER)); - - // assumes format from kernel: - // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes + reader = new BufferedReader(new FileReader(mProcStatsNetfilter)); - // skip first line, which is legend + // parse first line as header String line = reader.readLine(); - while ((line = reader.readLine()) != null) { - final StringTokenizer t = new StringTokenizer(line); + splitLine(line, keys); - final String idx = t.nextToken(); - final String iface = t.nextToken(); + // parse remaining lines + while ((line = reader.readLine()) != null) { + splitLine(line, values); + parseLine(keys, values, parsed); try { - // TODO: kernel currently emits tag in upper half of long; - // eventually switch to directly using int. - final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32); - final int uid = Integer.parseInt(t.nextToken()); - final long rx = Long.parseLong(t.nextToken()); - final long tx = Long.parseLong(t.nextToken()); + final String iface = parsed.get(KEY_IFACE); + final int tag = NetworkManagementSocketTagger.kernelToTag( + parsed.get(KEY_TAG_HEX)); + final int uid = Integer.parseInt(parsed.get(KEY_UID)); + final long rx = Long.parseLong(parsed.get(KEY_RX)); + final long tx = Long.parseLong(parsed.get(KEY_TX)); if (limitUid == UID_ALL || limitUid == uid) { stats.addEntry(iface, uid, tag, rx, tx); } } catch (NumberFormatException e) { - Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e); + Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); } } } catch (IOException e) { @@ -946,7 +1114,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { final String[] knownUids; if (limitUid == UID_ALL) { - knownUids = STATS_UIDSTAT.list(); + knownUids = mProcStatsUidstat.list(); } else { knownUids = new String[] { String.valueOf(limitUid) }; } @@ -955,7 +1123,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { SystemClock.elapsedRealtime(), knownUids.length); for (String uid : knownUids) { final int uidInt = Integer.parseInt(uid); - final File uidPath = new File(STATS_UIDSTAT, uid); + final File uidPath = new File(mProcStatsUidstat, uid); final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd")); stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx); @@ -1023,10 +1191,30 @@ class NetworkManagementService extends INetworkManagementService.Stub { return getInterfaceThrottle(iface, false); } - @Override - public void setBandwidthControlEnabled(boolean enabled) { - mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable"))); + /** + * Split given line into {@link ArrayList}. + */ + private static void splitLine(String line, ArrayList<String> outSplit) { + outSplit.clear(); + + final StringTokenizer t = new StringTokenizer(line); + while (t.hasMoreTokens()) { + outSplit.add(t.nextToken()); + } + } + + /** + * Zip the two given {@link ArrayList} as key and value pairs into + * {@link HashMap}. + */ + private static void parseLine( + ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) { + outParsed.clear(); + + final int size = Math.min(keys.size(), values.size()); + for (int i = 0; i < size; i++) { + outParsed.put(keys.get(i), values.get(i)); + } } /** @@ -1043,4 +1231,65 @@ class NetworkManagementService extends INetworkManagementService.Stub { return -1; } } + + public void setDefaultInterfaceForDns(String iface) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + try { + String cmd = "resolver setdefaultif " + iface; + + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating with native daemon to set default interface", e); + } + } + + public void setDnsServersForInterface(String iface, String[] servers) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE, + "NetworkManagementService"); + try { + String cmd = "resolver setifdns " + iface; + for (String s : servers) { + if (s != null && !"0.0.0.0".equals(s) && + !"::".equals(s) && !"0:0:0:0:0:0:0:0".equals(s)) { + cmd += " " + InetAddress.getByName(s).getHostAddress(); + } + } + + mConnector.doCommand(cmd); + } catch (UnknownHostException e) { + throw new IllegalStateException("failed to resolve dns address.", e); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating with native deamon to set dns for interface", e); + } + } + + public void flushDefaultDnsCache() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + try { + String cmd = "resolver flushdefaultif"; + + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating with native deamon to flush default interface", e); + } + } + + public void flushInterfaceDnsCache(String iface) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + try { + String cmd = "resolver flushif " + iface; + + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + throw new IllegalStateException( + "Error communicating with native deamon to flush interface " + iface, e); + } + } } diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java index 15f22c0..f7fe39e 100644 --- a/services/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/java/com/android/server/NetworkTimeUpdateService.java @@ -16,8 +16,6 @@ package com.android.server; -import com.android.internal.telephony.TelephonyIntents; - import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -28,7 +26,6 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.SntpClient; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -36,12 +33,10 @@ import android.os.Message; import android.os.SystemClock; import android.provider.Settings; import android.util.Log; -import android.util.Slog; +import android.util.NtpTrustedTime; +import android.util.TrustedTime; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Properties; +import com.android.internal.telephony.TelephonyIntents; /** * Monitors the network time and updates the system time if it is out of sync @@ -68,14 +63,11 @@ public class NetworkTimeUpdateService { private static final long POLLING_INTERVAL_SHORTER_MS = 60 * 1000L; // 60 seconds /** Number of times to try again */ private static final int TRY_AGAIN_TIMES_MAX = 3; - /** How long to wait for the NTP server to respond. */ - private static final int MAX_NTP_FETCH_WAIT_MS = 20 * 1000; /** If the time difference is greater than this threshold, then update the time. */ private static final int TIME_ERROR_THRESHOLD_MS = 5 * 1000; private static final String ACTION_POLL = "com.android.server.NetworkTimeUpdateService.action.POLL"; - private static final String PROPERTIES_FILE = "/etc/gps.conf"; private static int POLL_REQUEST = 0; private static final long NOT_SET = -1; @@ -84,14 +76,14 @@ public class NetworkTimeUpdateService { private long mNitzZoneSetTime = NOT_SET; private Context mContext; + private TrustedTime mTime; + // NTP lookup is done on this thread and handler private Handler mHandler; private HandlerThread mThread; private AlarmManager mAlarmManager; private PendingIntent mPendingPollIntent; private SettingsObserver mSettingsObserver; - // Address of the NTP server - private String mNtpServer; // The last time that we successfully fetched the NTP time. private long mLastNtpFetchTime = NOT_SET; // Keeps track of how many quick attempts were made to fetch NTP time. @@ -101,6 +93,7 @@ public class NetworkTimeUpdateService { public NetworkTimeUpdateService(Context context) { mContext = context; + mTime = NtpTrustedTime.getInstance(context); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); Intent pollIntent = new Intent(ACTION_POLL, null); mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); @@ -108,12 +101,6 @@ public class NetworkTimeUpdateService { /** Initialize the receivers and initiate the first NTP request */ public void systemReady() { - mNtpServer = getNtpServerAddress(); - if (mNtpServer == null) { - Slog.e(TAG, "NTP server address not found, not syncing to NTP time"); - return; - } - registerForTelephonyIntents(); registerForAlarms(); registerForConnectivityIntents(); @@ -128,27 +115,6 @@ public class NetworkTimeUpdateService { mSettingsObserver.observe(mContext); } - private String getNtpServerAddress() { - String serverAddress = null; - FileInputStream stream = null; - try { - Properties properties = new Properties(); - File file = new File(PROPERTIES_FILE); - stream = new FileInputStream(file); - properties.load(stream); - serverAddress = properties.getProperty("NTP_SERVER", null); - } catch (IOException e) { - Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (Exception e) {} - } - } - return serverAddress; - } - private void registerForTelephonyIntents() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME); @@ -189,9 +155,15 @@ public class NetworkTimeUpdateService { if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + POLLING_INTERVAL_MS || event == EVENT_AUTO_TIME_CHANGED) { if (DBG) Log.d(TAG, "Before Ntp fetch"); - long ntp = getNtpTime(); - if (DBG) Log.d(TAG, "Ntp = " + ntp); - if (ntp > 0) { + + // force refresh NTP cache when outdated + if (mTime.getCacheAge() >= POLLING_INTERVAL_MS) { + mTime.forceRefresh(); + } + + // only update when NTP time is fresh + if (mTime.getCacheAge() < POLLING_INTERVAL_MS) { + final long ntp = mTime.currentTimeMillis(); mTryAgainCounter = 0; mLastNtpFetchTime = SystemClock.elapsedRealtime(); if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS) { @@ -232,15 +204,6 @@ public class NetworkTimeUpdateService { mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent); } - private long getNtpTime() { - SntpClient client = new SntpClient(); - if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT_MS)) { - return client.getNtpTime(); - } else { - return 0; - } - } - /** * Checks if the user prefers to automatically set the time. */ diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java index 08c6699..950f3b6 100644 --- a/services/java/com/android/server/SystemBackupAgent.java +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -138,7 +138,7 @@ public class SystemBackupAgent extends BackupAgentHelper { if (outFile == null) { Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]"); } - FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true); + FullBackup.restoreFile(data, size, type, mode, mtime, outFile); if (restoredWallpaper) { WallpaperManagerService wallpaper = diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a23bacf..8c7e279 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -209,9 +209,11 @@ class ServerThread extends Thread { bluetoothA2dp); bluetooth.initAfterA2dpRegistration(); + int airplaneModeOn = Settings.System.getInt(mContentResolver, + Settings.System.AIRPLANE_MODE_ON, 0); int bluetoothOn = Settings.Secure.getInt(mContentResolver, Settings.Secure.BLUETOOTH_ON, 0); - if (bluetoothOn > 0) { + if (airplaneModeOn == 0 && bluetoothOn != 0) { bluetooth.enable(); } } @@ -281,7 +283,8 @@ class ServerThread extends Thread { try { Slog.i(TAG, "NetworkPolicy Service"); networkPolicy = new NetworkPolicyManagerService( - context, ActivityManagerService.self(), power, networkStats); + context, ActivityManagerService.self(), power, + networkStats, networkManagement); ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy); } catch (Throwable e) { Slog.e(TAG, "Failure starting NetworkPolicy Service", e); @@ -520,6 +523,7 @@ class ServerThread extends Thread { // These are needed to propagate to the runnable below. final Context contextF = context; final BatteryService batteryF = battery; + final NetworkManagementService networkManagementF = networkManagement; final NetworkStatsService networkStatsF = networkStats; final NetworkPolicyManagerService networkPolicyF = networkPolicy; final ConnectivityService connectivityF = connectivity; @@ -547,6 +551,7 @@ class ServerThread extends Thread { startSystemUi(contextF); if (batteryF != null) batteryF.systemReady(); + if (networkManagementF != null) networkManagementF.systemReady(); if (networkStatsF != null) networkStatsF.systemReady(); if (networkPolicyF != null) networkPolicyF.systemReady(); if (connectivityF != null) connectivityF.systemReady(); diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index 7266d7d..24d4dd3 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -16,9 +16,6 @@ package com.android.server; -import com.android.internal.R; -import com.android.internal.telephony.TelephonyProperties; - import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; @@ -54,6 +51,9 @@ import android.util.NtpTrustedTime; import android.util.Slog; import android.util.TrustedTime; +import com.android.internal.R; +import com.android.internal.telephony.TelephonyProperties; + import java.io.BufferedWriter; import java.io.File; import java.io.FileDescriptor; @@ -63,7 +63,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Calendar; import java.util.GregorianCalendar; -import java.util.Properties; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -87,7 +86,6 @@ public class ThrottleService extends IThrottleManager.Stub { private static final long TESTING_THRESHOLD = 1 * 1024 * 1024; private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000; - private static final long MAX_NTP_FETCH_WAIT = 20 * 1000; private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE; @@ -127,8 +125,6 @@ public class ThrottleService extends IThrottleManager.Stub { private static final int THROTTLE_INDEX_UNINITIALIZED = -1; private static final int THROTTLE_INDEX_UNTHROTTLED = 0; - private static final String PROPERTIES_FILE = "/etc/gps.conf"; - private Intent mPollStickyBroadcast; private TrustedTime mTime; @@ -139,8 +135,7 @@ public class ThrottleService extends IThrottleManager.Stub { } public ThrottleService(Context context) { - // TODO: move to using cached NtpTrustedTime - this(context, getNetworkManagementService(), new NtpTrustedTime(), + this(context, getNetworkManagementService(), NtpTrustedTime.getInstance(context), context.getResources().getString(R.string.config_datause_iface)); } @@ -179,14 +174,17 @@ public class ThrottleService extends IThrottleManager.Stub { mIface = iface; } - public void interfaceLinkStatusChanged(String iface, boolean link) { - if (link) { + public void interfaceStatusChanged(String iface, boolean up) { + if (up) { if (TextUtils.equals(iface, mIface)) { mHandler.obtainMessage(mMsg).sendToTarget(); } } } + public void interfaceLinkStateChanged(String iface, boolean up) { + } + public void interfaceAdded(String iface) { // TODO - an interface added in the UP state should also trigger a StatusChanged // notification.. @@ -338,26 +336,6 @@ public class ThrottleService extends IThrottleManager.Stub { } }, new IntentFilter(ACTION_RESET)); - FileInputStream stream = null; - try { - Properties properties = new Properties(); - File file = new File(PROPERTIES_FILE); - stream = new FileInputStream(file); - properties.load(stream); - final String ntpServer = properties.getProperty("NTP_SERVER", null); - if (mTime instanceof NtpTrustedTime) { - ((NtpTrustedTime) mTime).setNtpServer(ntpServer, MAX_NTP_FETCH_WAIT); - } - } catch (IOException e) { - Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (Exception e) {} - } - } - // use a new thread as we don't want to stall the system for file writes mThread = new HandlerThread(TAG); mThread.start(); diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index cb55451..5f0922e 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -36,6 +36,7 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiStateMachine; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.net.wifi.WifiWatchdogService; import android.net.wifi.WpsConfiguration; import android.net.wifi.WpsResult; import android.net.ConnectivityManager; @@ -342,6 +343,7 @@ public class WifiService extends IWifiManager.Stub { * Protected by mWifiStateTracker lock. */ private final WorkSource mTmpWorkSource = new WorkSource(); + private WifiWatchdogService mWifiWatchdogService; WifiService(Context context) { mContext = context; @@ -431,6 +433,9 @@ public class WifiService extends IWifiManager.Stub { Slog.i(TAG, "WifiService starting up with Wi-Fi " + (wifiEnabled ? "enabled" : "disabled")); setWifiEnabled(wifiEnabled); + + //TODO: as part of WWS refactor, create only when needed + mWifiWatchdogService = new WifiWatchdogService(mContext); } private boolean testAndClearWifiSavedState() { @@ -1155,6 +1160,10 @@ public class WifiService extends IWifiManager.Stub { pw.println(); pw.println("Locks held:"); mLocks.dump(pw); + + pw.println(); + pw.println("WifiWatchdogService dump"); + mWifiWatchdogService.dump(pw); } private class WifiLock extends DeathRecipient { diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java deleted file mode 100644 index 56bfbe0..0000000 --- a/services/java/com/android/server/WifiWatchdogService.java +++ /dev/null @@ -1,1450 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.database.ContentObserver; -import android.net.ConnectivityManager; -import android.net.LinkProperties; -import android.net.NetworkInfo; -import android.net.wifi.ScanResult; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Slog; - -import java.io.BufferedInputStream; -import java.io.InputStream; -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; -import java.net.URL; -import java.util.Collection; -import java.util.List; -import java.util.Random; -import java.util.Scanner; - -/** - * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi - * network with multiple access points. After the framework successfully - * connects to an access point, the watchdog verifies whether the DNS server is - * reachable. If not, the watchdog blacklists the current access point, leading - * to a connection on another access point within the same network. - * <p> - * The watchdog has a few safeguards: - * <ul> - * <li>Only monitor networks with multiple access points - * <li>Only check at most {@link #getMaxApChecks()} different access points - * within the network before giving up - * <p> - * The watchdog checks for connectivity on an access point by ICMP pinging the - * DNS. There are settings that allow disabling the watchdog, or tweaking the - * acceptable packet loss (and other various parameters). - * <p> - * The core logic of the watchdog is done on the main watchdog thread. Wi-Fi - * callbacks can come in on other threads, so we must queue messages to the main - * watchdog thread's handler. Most (if not all) state is only written to from - * the main thread. - * - * {@hide} - */ -public class WifiWatchdogService { - private static final String TAG = "WifiWatchdogService"; - private static final boolean V = false; - private static final boolean D = true; - - private Context mContext; - private ContentResolver mContentResolver; - private WifiManager mWifiManager; - private ConnectivityManager mConnectivityManager; - - /** - * The main watchdog thread. - */ - private WifiWatchdogThread mThread; - /** - * The handler for the main watchdog thread. - */ - private WifiWatchdogHandler mHandler; - - private ContentObserver mContentObserver; - - /** - * The current watchdog state. Only written from the main thread! - */ - private WatchdogState mState = WatchdogState.IDLE; - /** - * The SSID of the network that the watchdog is currently monitoring. Only - * touched in the main thread! - */ - private String mSsid; - /** - * The number of access points in the current network ({@link #mSsid}) that - * have been checked. Only touched in the main thread, using getter/setter methods. - */ - private int mBssidCheckCount; - /** Whether the current AP check should be canceled. */ - private boolean mShouldCancel; - - WifiWatchdogService(Context context) { - mContext = context; - mContentResolver = context.getContentResolver(); - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - createThread(); - - // The content observer to listen needs a handler, which createThread creates - registerForSettingsChanges(); - if (isWatchdogEnabled()) { - registerForWifiBroadcasts(); - } - - if (V) { - myLogV("WifiWatchdogService: Created"); - } - } - - /** - * Observes the watchdog on/off setting, and takes action when changed. - */ - private void registerForSettingsChanges() { - ContentResolver contentResolver = mContext.getContentResolver(); - contentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), false, - mContentObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - if (isWatchdogEnabled()) { - registerForWifiBroadcasts(); - } else { - unregisterForWifiBroadcasts(); - if (mHandler != null) { - mHandler.disableWatchdog(); - } - } - } - }); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON - */ - private boolean isWatchdogEnabled() { - return Settings.Secure.getInt(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_AP_COUNT - */ - private int getApCount() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_AP_COUNT, 2); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT - */ - private int getInitialIgnoredPingCount() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT , 2); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_COUNT - */ - private int getPingCount() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_PING_COUNT, 4); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_TIMEOUT_MS - */ - private int getPingTimeoutMs() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS, 500); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_DELAY_MS - */ - private int getPingDelayMs() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED - */ - private Boolean isWalledGardenTestEnabled() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL - */ - private String getWalledGardenUrl() { - String url = Settings.Secure.getString(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL); - if (TextUtils.isEmpty(url)) return "http://www.google.com/"; - return url; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN - */ - private String getWalledGardenPattern() { - String pattern = Settings.Secure.getString(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN); - if (TextUtils.isEmpty(pattern)) return "<title>.*Google.*</title>"; - return pattern; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE - */ - private int getAcceptablePacketLossPercentage() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE, 25); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS - */ - private int getMaxApChecks() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_MAX_AP_CHECKS, 7); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED - */ - private boolean isBackgroundCheckEnabled() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED, 1) == 1; - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS - */ - private int getBackgroundCheckDelayMs() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS, 60000); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS - */ - private int getBackgroundCheckTimeoutMs() { - return Settings.Secure.getInt(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS, 1000); - } - - /** - * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WATCH_LIST - * @return the comma-separated list of SSIDs - */ - private String getWatchList() { - return Settings.Secure.getString(mContentResolver, - Settings.Secure.WIFI_WATCHDOG_WATCH_LIST); - } - - /** - * Registers to receive the necessary Wi-Fi broadcasts. - */ - private void registerForWifiBroadcasts() { - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - mContext.registerReceiver(mReceiver, intentFilter); - } - - /** - * Unregisters from receiving the Wi-Fi broadcasts. - */ - private void unregisterForWifiBroadcasts() { - mContext.unregisterReceiver(mReceiver); - } - - /** - * Creates the main watchdog thread, including waiting for the handler to be - * created. - */ - private void createThread() { - mThread = new WifiWatchdogThread(); - mThread.start(); - waitForHandlerCreation(); - } - - /** - * Unregister broadcasts and quit the watchdog thread - */ - //TODO: Change back to running WWS when needed -// private void quit() { -// unregisterForWifiBroadcasts(); -// mContext.getContentResolver().unregisterContentObserver(mContentObserver); -// mHandler.removeAllActions(); -// mHandler.getLooper().quit(); -// } - - /** - * Waits for the main watchdog thread to create the handler. - */ - private void waitForHandlerCreation() { - synchronized(this) { - while (mHandler == null) { - try { - // Wait for the handler to be set by the other thread - wait(); - } catch (InterruptedException e) { - Slog.e(TAG, "Interrupted while waiting on handler."); - } - } - } - } - - // Utility methods - - /** - * Logs with the current thread. - */ - private static void myLogV(String message) { - Slog.v(TAG, "(" + Thread.currentThread().getName() + ") " + message); - } - - private static void myLogD(String message) { - Slog.d(TAG, "(" + Thread.currentThread().getName() + ") " + message); - } - - /** - * Gets the first DNS of the current AP. - * - * @return The first DNS of the current AP. - */ - private InetAddress getDns() { - if (mConnectivityManager == null) { - mConnectivityManager = (ConnectivityManager)mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - } - - LinkProperties linkProperties = mConnectivityManager.getLinkProperties( - ConnectivityManager.TYPE_WIFI); - if (linkProperties == null) return null; - - Collection<InetAddress> dnses = linkProperties.getDnses(); - if (dnses == null || dnses.size() == 0) return null; - - return dnses.iterator().next(); - } - - /** - * Checks whether the DNS can be reached using multiple attempts according - * to the current setting values. - * - * @return Whether the DNS is reachable - */ - private boolean checkDnsConnectivity() { - InetAddress dns = getDns(); - if (dns == null) { - if (V) { - myLogV("checkDnsConnectivity: Invalid DNS, returning false"); - } - return false; - } - - if (V) { - myLogV("checkDnsConnectivity: Checking " + dns.getHostAddress() + " for connectivity"); - } - - int numInitialIgnoredPings = getInitialIgnoredPingCount(); - int numPings = getPingCount(); - int pingDelay = getPingDelayMs(); - int acceptableLoss = getAcceptablePacketLossPercentage(); - - /** See {@link Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} */ - int ignoredPingCounter = 0; - int pingCounter = 0; - int successCounter = 0; - - // No connectivity check needed - if (numPings == 0) { - return true; - } - - // Do the initial pings that we ignore - for (; ignoredPingCounter < numInitialIgnoredPings; ignoredPingCounter++) { - if (shouldCancel()) return false; - - boolean dnsAlive = DnsPinger.isDnsReachable(dns, getPingTimeoutMs()); - if (dnsAlive) { - /* - * Successful "ignored" pings are *not* ignored (they count in the total number - * of pings), but failures are really ignored. - */ - - // TODO: This is confusing logic and should be rewitten - // Here, successful 'ignored' pings are interpreted as a success in the below loop - pingCounter++; - successCounter++; - } - - if (V) { - Slog.v(TAG, (dnsAlive ? " +" : " Ignored: -")); - } - - if (shouldCancel()) return false; - - try { - Thread.sleep(pingDelay); - } catch (InterruptedException e) { - Slog.w(TAG, "Interrupted while pausing between pings", e); - } - } - - // Do the pings that we use to measure packet loss - for (; pingCounter < numPings; pingCounter++) { - if (shouldCancel()) return false; - - if (DnsPinger.isDnsReachable(dns, getPingTimeoutMs())) { - successCounter++; - if (V) { - Slog.v(TAG, " +"); - } - } else { - if (V) { - Slog.v(TAG, " -"); - } - } - - if (shouldCancel()) return false; - - try { - Thread.sleep(pingDelay); - } catch (InterruptedException e) { - Slog.w(TAG, "Interrupted while pausing between pings", e); - } - } - - //TODO: Integer division might cause problems down the road... - int packetLossPercentage = 100 * (numPings - successCounter) / numPings; - if (D) { - Slog.d(TAG, packetLossPercentage - + "% packet loss (acceptable is " + acceptableLoss + "%)"); - } - - return !shouldCancel() && (packetLossPercentage <= acceptableLoss); - } - - private boolean backgroundCheckDnsConnectivity() { - InetAddress dns = getDns(); - - if (dns == null) { - if (V) { - myLogV("backgroundCheckDnsConnectivity: DNS is empty, returning false"); - } - return false; - } - - if (V) { - myLogV("backgroundCheckDnsConnectivity: Background checking " + - dns.getHostAddress() + " for connectivity"); - } - - return DnsPinger.isDnsReachable(dns, getBackgroundCheckTimeoutMs()); - } - - /** - * Signals the current action to cancel. - */ - private void cancelCurrentAction() { - mShouldCancel = true; - } - - /** - * Helper to check whether to cancel. - * - * @return Whether to cancel processing the action. - */ - private boolean shouldCancel() { - if (V && mShouldCancel) { - myLogV("shouldCancel: Cancelling"); - } - - return mShouldCancel; - } - - // Wi-Fi initiated callbacks (could be executed in another thread) - - /** - * Called when connected to an AP (this can be the next AP in line, or - * it can be a completely different network). - * - * @param ssid The SSID of the access point. - * @param bssid The BSSID of the access point. - */ - private void onConnected(String ssid, String bssid) { - if (V) { - myLogV("onConnected: SSID: " + ssid + ", BSSID: " + bssid); - } - - /* - * The current action being processed by the main watchdog thread is now - * stale, so cancel it. - */ - cancelCurrentAction(); - - if ((mSsid == null) || !mSsid.equals(ssid)) { - /* - * This is a different network than what the main watchdog thread is - * processing, dispatch the network change message on the main thread. - */ - mHandler.dispatchNetworkChanged(ssid); - } - - if (requiresWatchdog(ssid, bssid)) { - if (D) { - myLogD(ssid + " (" + bssid + ") requires the watchdog"); - } - - // This access point requires a watchdog, so queue the check on the main thread - mHandler.checkAp(new AccessPoint(ssid, bssid)); - - } else { - if (D) { - myLogD(ssid + " (" + bssid + ") does not require the watchdog"); - } - - // This access point does not require a watchdog, so queue idle on the main thread - mHandler.idle(); - } - if (isWalledGardenTestEnabled()) mHandler.checkWalledGarden(ssid); - } - - /** - * Called when Wi-Fi is enabled. - */ - private void onEnabled() { - cancelCurrentAction(); - // Queue a hard-reset of the state on the main thread - mHandler.reset(); - } - - /** - * Called when disconnected (or some other event similar to being disconnected). - */ - private void onDisconnected() { - if (V) { - myLogV("onDisconnected"); - } - - /* - * Disconnected from an access point, the action being processed by the - * watchdog thread is now stale, so cancel it. - */ - cancelCurrentAction(); - // Dispatch the disconnected to the main watchdog thread - mHandler.dispatchDisconnected(); - // Queue the action to go idle - mHandler.idle(); - } - - /** - * Checks whether an access point requires watchdog monitoring. - * - * @param ssid The SSID of the access point. - * @param bssid The BSSID of the access point. - * @return Whether the access point/network should be monitored by the - * watchdog. - */ - private boolean requiresWatchdog(String ssid, String bssid) { - if (V) { - myLogV("requiresWatchdog: SSID: " + ssid + ", BSSID: " + bssid); - } - - WifiInfo info = null; - if (ssid == null) { - /* - * This is called from a Wi-Fi callback, so assume the WifiInfo does - * not have stale data. - */ - info = mWifiManager.getConnectionInfo(); - ssid = info.getSSID(); - if (ssid == null) { - // It's still null, give up - if (V) { - Slog.v(TAG, " Invalid SSID, returning false"); - } - return false; - } - } - - if (TextUtils.isEmpty(bssid)) { - // Similar as above - if (info == null) { - info = mWifiManager.getConnectionInfo(); - } - bssid = info.getBSSID(); - if (TextUtils.isEmpty(bssid)) { - // It's still null, give up - if (V) { - Slog.v(TAG, " Invalid BSSID, returning false"); - } - return false; - } - } - - if (!isOnWatchList(ssid)) { - if (V) { - Slog.v(TAG, " SSID not on watch list, returning false"); - } - return false; - } - - // The watchdog only monitors networks with multiple APs - if (!hasRequiredNumberOfAps(ssid)) { - return false; - } - - return true; - } - - private boolean isOnWatchList(String ssid) { - String watchList; - - if (ssid == null || (watchList = getWatchList()) == null) { - return false; - } - - String[] list = watchList.split(" *, *"); - - for (String name : list) { - if (ssid.equals(name)) { - return true; - } - } - - return false; - } - - /** - * Checks if the current scan results have multiple access points with an SSID. - * - * @param ssid The SSID to check. - * @return Whether the SSID has multiple access points. - */ - private boolean hasRequiredNumberOfAps(String ssid) { - List<ScanResult> results = mWifiManager.getScanResults(); - if (results == null) { - if (V) { - myLogV("hasRequiredNumberOfAps: Got null scan results, returning false"); - } - return false; - } - - int numApsRequired = getApCount(); - int numApsFound = 0; - int resultsSize = results.size(); - for (int i = 0; i < resultsSize; i++) { - ScanResult result = results.get(i); - if (result == null) continue; - if (result.SSID == null) continue; - - if (result.SSID.equals(ssid)) { - numApsFound++; - - if (numApsFound >= numApsRequired) { - if (V) { - myLogV("hasRequiredNumberOfAps: SSID: " + ssid + ", returning true"); - } - return true; - } - } - } - - if (V) { - myLogV("hasRequiredNumberOfAps: SSID: " + ssid + ", returning false"); - } - return false; - } - - // Watchdog logic (assume all of these methods will be in our main thread) - - /** - * Handles a Wi-Fi network change (for example, from networkA to networkB). - */ - private void handleNetworkChanged(String ssid) { - // Set the SSID being monitored to the new SSID - mSsid = ssid; - // Set various state to that when being idle - setIdleState(true); - } - - /** - * Handles checking whether an AP is a "good" AP. If not, it will be blacklisted. - * - * @param ap The access point to check. - */ - private void handleCheckAp(AccessPoint ap) { - // Reset the cancel state since this is the entry point of this action - mShouldCancel = false; - - if (V) { - myLogV("handleCheckAp: AccessPoint: " + ap); - } - - // Make sure we are not sleeping - if (mState == WatchdogState.SLEEP) { - if (V) { - Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning"); - } - return; - } - - mState = WatchdogState.CHECKING_AP; - - /* - * Checks to make sure we haven't exceeded the max number of checks - * we're allowed per network - */ - incrementBssidCheckCount(); - if (getBssidCheckCount() > getMaxApChecks()) { - if (V) { - Slog.v(TAG, " Passed the max attempts (" + getMaxApChecks() - + "), going to sleep for " + mSsid); - } - mHandler.sleep(mSsid); - return; - } - - // Do the check - boolean isApAlive = checkDnsConnectivity(); - - if (V) { - Slog.v(TAG, " Is it alive: " + isApAlive); - } - - // Take action based on results - if (isApAlive) { - handleApAlive(ap); - } else { - handleApUnresponsive(ap); - } - } - - /** - * Handles the case when an access point is alive. - * - * @param ap The access point. - */ - private void handleApAlive(AccessPoint ap) { - // Check whether we are stale and should cancel - if (shouldCancel()) return; - // We're satisfied with this AP, so go idle - setIdleState(false); - - if (D) { - myLogD("AP is alive: " + ap.toString()); - } - - // Queue the next action to be a background check - mHandler.backgroundCheckAp(ap); - } - - /** - * Handles an unresponsive AP by blacklisting it. - * - * @param ap The access point. - */ - private void handleApUnresponsive(AccessPoint ap) { - // Check whether we are stale and should cancel - if (shouldCancel()) return; - // This AP is "bad", switch to another - mState = WatchdogState.SWITCHING_AP; - - if (D) { - myLogD("AP is dead: " + ap.toString()); - } - - // Black list this "bad" AP, this will cause an attempt to connect to another - blacklistAp(ap.bssid); - // Initiate an association to an alternate AP - mWifiManager.reassociate(); - } - - private void blacklistAp(String bssid) { - if (TextUtils.isEmpty(bssid)) { - return; - } - - // Before taking action, make sure we should not cancel our processing - if (shouldCancel()) return; - - mWifiManager.addToBlacklist(bssid); - - if (D) { - myLogD("Blacklisting " + bssid); - } - } - - /** - * Handles a single background check. If it fails, it should trigger a - * normal check. If it succeeds, it should queue another background check. - * - * @param ap The access point to do a background check for. If this is no - * longer the current AP, it is okay to return without any - * processing. - */ - private void handleBackgroundCheckAp(AccessPoint ap) { - // Reset the cancel state since this is the entry point of this action - mShouldCancel = false; - - if (V) { - myLogV("handleBackgroundCheckAp: AccessPoint: " + ap); - } - - // Make sure we are not sleeping - if (mState == WatchdogState.SLEEP) { - if (V) { - Slog.v(TAG, " handleBackgroundCheckAp: Sleeping (in " + mSsid + "), so returning"); - } - return; - } - - // Make sure the AP we're supposed to be background checking is still the active one - WifiInfo info = mWifiManager.getConnectionInfo(); - if (info.getSSID() == null || !info.getSSID().equals(ap.ssid)) { - if (V) { - myLogV("handleBackgroundCheckAp: We are no longer connected to " - + ap + ", and instead are on " + info); - } - return; - } - - if (info.getBSSID() == null || !info.getBSSID().equals(ap.bssid)) { - if (V) { - myLogV("handleBackgroundCheckAp: We are no longer connected to " - + ap + ", and instead are on " + info); - } - return; - } - - // Do the check - boolean isApAlive = backgroundCheckDnsConnectivity(); - - if (V && !isApAlive) { - Slog.v(TAG, " handleBackgroundCheckAp: Is it alive: " + isApAlive); - } - - if (shouldCancel()) { - return; - } - - // Take action based on results - if (isApAlive) { - // Queue another background check - mHandler.backgroundCheckAp(ap); - - } else { - if (D) { - myLogD("Background check failed for " + ap.toString()); - } - - // Queue a normal check, so it can take proper action - mHandler.checkAp(ap); - } - } - - /** - * Handles going to sleep for this network. Going to sleep means we will not - * monitor this network anymore. - * - * @param ssid The network that will not be monitored anymore. - */ - private void handleSleep(String ssid) { - // Make sure the network we're trying to sleep in is still the current network - if (ssid != null && ssid.equals(mSsid)) { - mState = WatchdogState.SLEEP; - - if (D) { - myLogD("Going to sleep for " + ssid); - } - - /* - * Before deciding to go to sleep, we may have checked a few APs - * (and blacklisted them). Clear the blacklist so the AP with best - * signal is chosen. - */ - mWifiManager.clearBlacklist(); - - if (V) { - myLogV("handleSleep: Set state to SLEEP and cleared blacklist"); - } - } - } - - /** - * Handles an access point disconnection. - */ - private void handleDisconnected() { - /* - * We purposefully do not change mSsid to null. This is to handle - * disconnected followed by connected better (even if there is some - * duration in between). For example, if the watchdog went to sleep in a - * network, and then the phone goes to sleep, when the phone wakes up we - * still want to be in the sleeping state. When the phone went to sleep, - * we would have gotten a disconnected event which would then set mSsid - * = null. This is bad, since the following connect would cause us to do - * the "network is good?" check all over again. */ - - /* - * Set the state as if we were idle (don't come out of sleep, only - * hard reset and network changed should do that. - */ - setIdleState(false); - } - - /** - * Handles going idle. Idle means we are satisfied with the current state of - * things, but if a new connection occurs we'll re-evaluate. - */ - private void handleIdle() { - // Reset the cancel state since this is the entry point for this action - mShouldCancel = false; - - if (V) { - myLogV("handleSwitchToIdle"); - } - - // If we're sleeping, don't do anything - if (mState == WatchdogState.SLEEP) { - Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning"); - return; - } - - // Set the idle state - setIdleState(false); - - if (V) { - Slog.v(TAG, " Set state to IDLE"); - } - } - - /** - * Sets the state as if we are going idle. - */ - private void setIdleState(boolean forceIdleState) { - // Setting idle state does not kick us out of sleep unless the forceIdleState is set - if (forceIdleState || (mState != WatchdogState.SLEEP)) { - mState = WatchdogState.IDLE; - } - resetBssidCheckCount(); - } - - /** - * Handles a hard reset. A hard reset is rarely used, but when used it - * should revert anything done by the watchdog monitoring. - */ - private void handleReset() { - mWifiManager.clearBlacklist(); - setIdleState(true); - } - - // Inner classes - - /** - * Possible states for the watchdog to be in. - */ - private static enum WatchdogState { - /** The watchdog is currently idle, but it is still responsive to future AP checks in this network. */ - IDLE, - /** The watchdog is sleeping, so it will not try any AP checks for the network. */ - SLEEP, - /** The watchdog is currently checking an AP for connectivity. */ - CHECKING_AP, - /** The watchdog is switching to another AP in the network. */ - SWITCHING_AP - } - - private int getBssidCheckCount() { - return mBssidCheckCount; - } - - private void incrementBssidCheckCount() { - mBssidCheckCount++; - } - - private void resetBssidCheckCount() { - this.mBssidCheckCount = 0; - } - - /** - * The main thread for the watchdog monitoring. This will be turned into a - * {@link Looper} thread. - */ - private class WifiWatchdogThread extends Thread { - WifiWatchdogThread() { - super("WifiWatchdogThread"); - } - - @Override - public void run() { - // Set this thread up so the handler will work on it - Looper.prepare(); - - synchronized(WifiWatchdogService.this) { - mHandler = new WifiWatchdogHandler(); - - // Notify that the handler has been created - WifiWatchdogService.this.notify(); - } - - // Listen for messages to the handler - Looper.loop(); - } - } - - /** - * The main thread's handler. There are 'actions', and just general - * 'messages'. There should only ever be one 'action' in the queue (aside - * from the one being processed, if any). There may be multiple messages in - * the queue. So, actions are replaced by more recent actions, where as - * messages will be executed for sure. Messages end up being used to just - * change some state, and not really take any action. - * <p> - * There is little logic inside this class, instead methods of the form - * "handle___" are called in the main {@link WifiWatchdogService}. - */ - private class WifiWatchdogHandler extends Handler { - /** Check whether the AP is "good". The object will be an {@link AccessPoint}. */ - static final int ACTION_CHECK_AP = 1; - /** Go into the idle state. */ - static final int ACTION_IDLE = 2; - /** - * Performs a periodic background check whether the AP is still "good". - * The object will be an {@link AccessPoint}. - */ - static final int ACTION_BACKGROUND_CHECK_AP = 3; - /** Check whether the connection is a walled garden */ - static final int ACTION_CHECK_WALLED_GARDEN = 4; - - /** - * Go to sleep for the current network. We are conservative with making - * this a message rather than action. We want to make sure our main - * thread sees this message, but if it were an action it could be - * removed from the queue and replaced by another action. The main - * thread will ensure when it sees the message that the state is still - * valid for going to sleep. - * <p> - * For an explanation of sleep, see {@link android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS}. - */ - static final int MESSAGE_SLEEP = 101; - /** Disables the watchdog. */ - static final int MESSAGE_DISABLE_WATCHDOG = 102; - /** The network has changed. */ - static final int MESSAGE_NETWORK_CHANGED = 103; - /** The current access point has disconnected. */ - static final int MESSAGE_DISCONNECTED = 104; - /** Performs a hard-reset on the watchdog state. */ - static final int MESSAGE_RESET = 105; - - /* Walled garden detection */ - private String mLastSsid; - private long mLastTime; - private final long MIN_WALLED_GARDEN_TEST_INTERVAL = 15 * 60 * 1000; //15 minutes - - void checkWalledGarden(String ssid) { - sendMessage(obtainMessage(ACTION_CHECK_WALLED_GARDEN, ssid)); - } - - void checkAp(AccessPoint ap) { - removeAllActions(); - sendMessage(obtainMessage(ACTION_CHECK_AP, ap)); - } - - void backgroundCheckAp(AccessPoint ap) { - if (!isBackgroundCheckEnabled()) return; - - removeAllActions(); - sendMessageDelayed(obtainMessage(ACTION_BACKGROUND_CHECK_AP, ap), - getBackgroundCheckDelayMs()); - } - - void idle() { - removeAllActions(); - sendMessage(obtainMessage(ACTION_IDLE)); - } - - void sleep(String ssid) { - removeAllActions(); - sendMessage(obtainMessage(MESSAGE_SLEEP, ssid)); - } - - void disableWatchdog() { - removeAllActions(); - sendMessage(obtainMessage(MESSAGE_DISABLE_WATCHDOG)); - } - - void dispatchNetworkChanged(String ssid) { - removeAllActions(); - sendMessage(obtainMessage(MESSAGE_NETWORK_CHANGED, ssid)); - } - - void dispatchDisconnected() { - removeAllActions(); - sendMessage(obtainMessage(MESSAGE_DISCONNECTED)); - } - - void reset() { - removeAllActions(); - sendMessage(obtainMessage(MESSAGE_RESET)); - } - - private void removeAllActions() { - removeMessages(ACTION_CHECK_AP); - removeMessages(ACTION_IDLE); - removeMessages(ACTION_BACKGROUND_CHECK_AP); - } - - @Override - public void handleMessage(Message msg) { - if (V) { - myLogV("handleMessage: " + msg.what); - } - switch (msg.what) { - case MESSAGE_NETWORK_CHANGED: - handleNetworkChanged((String) msg.obj); - break; - case ACTION_CHECK_AP: - handleCheckAp((AccessPoint) msg.obj); - break; - case ACTION_BACKGROUND_CHECK_AP: - handleBackgroundCheckAp((AccessPoint) msg.obj); - break; - case ACTION_CHECK_WALLED_GARDEN: - handleWalledGardenCheck((String) msg.obj); - break; - case MESSAGE_SLEEP: - handleSleep((String) msg.obj); - break; - case ACTION_IDLE: - handleIdle(); - break; - case MESSAGE_DISABLE_WATCHDOG: - handleIdle(); - break; - case MESSAGE_DISCONNECTED: - handleDisconnected(); - break; - case MESSAGE_RESET: - handleReset(); - break; - } - } - - /** - * DNS based detection techniques do not work at all hotspots. The one sure way to check - * a walled garden is to see if a URL fetch on a known address fetches the data we - * expect - */ - private boolean isWalledGardenConnection() { - InputStream in = null; - HttpURLConnection urlConnection = null; - try { - URL url = new URL(getWalledGardenUrl()); - urlConnection = (HttpURLConnection) url.openConnection(); - in = new BufferedInputStream(urlConnection.getInputStream()); - Scanner scanner = new Scanner(in); - if (scanner.findInLine(getWalledGardenPattern()) != null) { - return false; - } else { - return true; - } - } catch (IOException e) { - return false; - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - if (urlConnection != null) urlConnection.disconnect(); - } - } - - private void handleWalledGardenCheck(String ssid) { - long currentTime = System.currentTimeMillis(); - //Avoid a walled garden test on the same network if one was already done - //within MIN_WALLED_GARDEN_TEST_INTERVAL. This will handle scenarios where - //there are frequent network disconnections - if (ssid.equals(mLastSsid) && - (currentTime - mLastTime) < MIN_WALLED_GARDEN_TEST_INTERVAL) { - return; - } - - mLastTime = currentTime; - mLastSsid = ssid; - - if (isWalledGardenConnection()) { - Uri uri = Uri.parse("http://www.google.com"); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | - Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); - } - } - } - - /** - * Receives Wi-Fi broadcasts. - * <p> - * There is little logic in this class, instead methods of the form "on___" - * are called in the {@link WifiWatchdogService}. - */ - private BroadcastReceiver mReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - handleNetworkStateChanged( - (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)); - } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - handleWifiStateChanged(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN)); - } - } - - private void handleNetworkStateChanged(NetworkInfo info) { - if (V) { - myLogV("Receiver.handleNetworkStateChanged: NetworkInfo: " - + info); - } - - switch (info.getState()) { - case CONNECTED: - WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) { - if (V) { - myLogV("handleNetworkStateChanged: Got connected event but SSID or BSSID are null. SSID: " - + wifiInfo.getSSID() - + ", BSSID: " - + wifiInfo.getBSSID() + ", ignoring event"); - } - return; - } - onConnected(wifiInfo.getSSID(), wifiInfo.getBSSID()); - break; - - case DISCONNECTED: - onDisconnected(); - break; - } - } - - private void handleWifiStateChanged(int wifiState) { - if (wifiState == WifiManager.WIFI_STATE_DISABLED) { - onDisconnected(); - } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) { - onEnabled(); - } - } - }; - - /** - * Describes an access point by its SSID and BSSID. - * - */ - private static class AccessPoint { - String ssid; - String bssid; - - /** - * @param ssid cannot be null - * @param bssid cannot be null - */ - AccessPoint(String ssid, String bssid) { - if (ssid == null || bssid == null) { - Slog.e(TAG, String.format("(%s) INVALID ACCESSPOINT: (%s, %s)", - Thread.currentThread().getName(),ssid,bssid)); - } - this.ssid = ssid; - this.bssid = bssid; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof AccessPoint)) return false; - AccessPoint otherAp = (AccessPoint) o; - - // Either we both have a null, or our SSIDs and BSSIDs are equal - return ssid.equals(otherAp.ssid) && bssid.equals(otherAp.bssid); - } - - @Override - public int hashCode() { - return ssid.hashCode() + bssid.hashCode(); - } - - @Override - public String toString() { - return ssid + " (" + bssid + ")"; - } - } - - /** - * Performs a simple DNS "ping" by sending a "server status" query packet to - * the DNS server. As long as the server replies, we consider it a success. - * <p> - * We do not use a simple hostname lookup because that could be cached and - * the API may not differentiate between a time out and a failure lookup - * (which we really care about). - */ - private static class DnsPinger { - - /** Number of bytes for the query */ - private static final int DNS_QUERY_BASE_SIZE = 33; - - /** The DNS port */ - private static final int DNS_PORT = 53; - - /** Used to generate IDs */ - private static Random sRandom = new Random(); - - static boolean isDnsReachable(InetAddress dnsAddress, int timeout) { - DatagramSocket socket = null; - try { - socket = new DatagramSocket(); - - // Set some socket properties - socket.setSoTimeout(timeout); - - byte[] buf = new byte[DNS_QUERY_BASE_SIZE]; - fillQuery(buf); - - // Send the DNS query - - DatagramPacket packet = new DatagramPacket(buf, - buf.length, dnsAddress, DNS_PORT); - socket.send(packet); - - // Wait for reply (blocks for the above timeout) - DatagramPacket replyPacket = new DatagramPacket(buf, buf.length); - socket.receive(replyPacket); - - // If a timeout occurred, an exception would have been thrown. We got a reply! - return true; - - } catch (SocketException e) { - if (V) { - Slog.v(TAG, "DnsPinger.isReachable received SocketException", e); - } - return false; - - } catch (UnknownHostException e) { - if (V) { - Slog.v(TAG, "DnsPinger.isReachable is unable to resolve the DNS host", e); - } - return false; - - } catch (SocketTimeoutException e) { - return false; - - } catch (IOException e) { - if (V) { - Slog.v(TAG, "DnsPinger.isReachable got an IOException", e); - } - return false; - - } catch (Exception e) { - if (V) { - Slog.d(TAG, "DnsPinger.isReachable got an unknown exception", e); - } - return false; - } finally { - if (socket != null) { - socket.close(); - } - } - } - - private static void fillQuery(byte[] buf) { - - /* - * See RFC2929 (though the bit tables in there are misleading for - * us. For example, the recursion desired bit is the 0th bit for us, - * but looking there it would appear as the 7th bit of the byte - */ - - // Make sure it's all zeroed out - for (int i = 0; i < buf.length; i++) buf[i] = 0; - - // Form a query for www.android.com - - // [0-1] bytes are an ID, generate random ID for this query - buf[0] = (byte) sRandom.nextInt(256); - buf[1] = (byte) sRandom.nextInt(256); - - // [2-3] bytes are for flags. - buf[2] = 1; // Recursion desired - - // [4-5] bytes are for the query count - buf[5] = 1; // One query - - // [6-7] [8-9] [10-11] are all counts of other fields we don't use - - // [12-15] for www - writeString(buf, 12, "www"); - - // [16-23] for android - writeString(buf, 16, "android"); - - // [24-27] for com - writeString(buf, 24, "com"); - - // [29-30] bytes are for QTYPE, set to 1 - buf[30] = 1; - - // [31-32] bytes are for QCLASS, set to 1 - buf[32] = 1; - } - - private static void writeString(byte[] buf, int startPos, String string) { - int pos = startPos; - - // Write the length first - buf[pos++] = (byte) string.length(); - for (int i = 0; i < string.length(); i++) { - buf[pos++] = (byte) string.charAt(i); - } - } - } -} diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index f6dd43a..80cdf6b 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -436,7 +436,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public IAccessibilityServiceConnection registerEventListener(IEventListener listener) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT, FUNCTION_REGISTER_EVENT_LISTENER); - ComponentName componentName = new ComponentName("foo.bar", "FakeAccessibilityService"); + ComponentName componentName = new ComponentName("foo.bar", + "AutomationAccessibilityService"); synchronized (mLock) { Service oldService = mComponentNameToServiceMap.get(componentName); if (oldService != null) { @@ -550,6 +551,26 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyEventListenerLocked(Service service, int eventType) { IEventListener listener = service.mServiceInterface; AccessibilityEvent event = service.mPendingEvents.get(eventType); + + // Check for null here because there is a concurrent scenario in which this + // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked + // which posts a message for dispatching an event. 2) The message is pulled + // from the queue by the handler on the service thread and the latter is + // just about to acquire the lock and call this method. 3) Now another binder + // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked + // so the service thread waits for the lock; 4) The binder thread replaces + // the event with a more recent one (assume the same event type) and posts a + // dispatch request releasing the lock. 5) Now the main thread is unblocked and + // dispatches the event which is removed from the pending ones. 6) And ... now + // the service thread handles the last message posted by the last binder call + // but the event is already dispatched and hence looking it up in the pending + // ones yields null. This check is much simpler that keeping count for each + // event type of each service to catch such a scenario since only one message + // is processed at a time. + if (event == null) { + return; + } + service.mPendingEvents.remove(eventType); try { if (mSecurityPolicy.canRetrieveWindowContent(service)) { @@ -809,7 +830,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub boolean mCanRetrieveScreenContent; - boolean mIsFake; + boolean mIsAutomation; final Callback mCallback = new Callback(); @@ -822,12 +843,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub new SparseArray<AccessibilityEvent>(); public Service(ComponentName componentName, - AccessibilityServiceInfo accessibilityServiceInfo, boolean isFake) { + AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) { mId = sIdCounter++; mComponentName = componentName; mAccessibilityServiceInfo = accessibilityServiceInfo; - mIsFake = isFake; - if (!isFake) { + mIsAutomation = isAutomation; + if (!isAutomation) { mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent(); mIntent = new Intent().setComponent(mComponentName); mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, @@ -861,7 +882,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * @return True if binding is successful. */ public boolean bind() { - if (!mIsFake && mService == null) { + if (!mIsAutomation && mService == null) { return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE); } return false; @@ -878,7 +899,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { tryRemoveServiceLocked(this); } - if (!mIsFake) { + if (!mIsAutomation) { mContext.unbindService(this); } mService = null; @@ -918,16 +939,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub IAccessibilityInteractionConnection connection = null; synchronized (mLock) { final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); - if (permissionGranted) { + if (!permissionGranted) { + return null; + } else { connection = getConnectionToRetrievalAllowingWindowLocked(); + if (connection == null) { + if (DEBUG) { + Slog.e(LOG_TAG, "No interaction connection to a retrieve " + + "allowing window."); + } + return null; + } } } - if (connection == null) { - if (DEBUG) { - Slog.e(LOG_TAG, "No interaction connection to a retrieve allowing window."); - } - return null; - } final long identityToken = Binder.clearCallingIdentity(); try { final int interactionId = mInteractionIdCounter.getAndIncrement(); @@ -962,16 +986,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId); - if (permissionGranted) { + if (!permissionGranted) { + return null; + } else { connection = getConnectionToRetrievalAllowingWindowLocked(); + if (connection == null) { + if (DEBUG) { + Slog.e(LOG_TAG, "No interaction connection to focused window."); + } + return null; + } } } - if (connection == null) { - if (DEBUG) { - Slog.e(LOG_TAG, "No interaction connection to focused window."); - } - return null; - } final long identityToken = Binder.clearCallingIdentity(); try { final int interactionId = mInteractionIdCounter.getAndIncrement(); @@ -1005,17 +1031,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId); - if (permissionGranted) { + if (!permissionGranted) { + return null; + } else { connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId); + if (connection == null) { + if (DEBUG) { + Slog.e(LOG_TAG, "No interaction connection to window: " + + accessibilityWindowId); + } + return null; + } } } - if (connection == null) { - if (DEBUG) { - Slog.e(LOG_TAG, "No interaction connection to window: " - + accessibilityWindowId); - } - return null; - } final long identityToken = Binder.clearCallingIdentity(); try { final int interactionId = mInteractionIdCounter.getAndIncrement(); @@ -1046,17 +1074,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this, accessibilityWindowId, action); - if (permissionGranted) { + if (!permissionGranted) { + return false; + } else { connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId); + if (connection == null) { + if (DEBUG) { + Slog.e(LOG_TAG, "No interaction connection to window: " + + accessibilityWindowId); + } + return false; + } } } - if (connection == null) { - if (DEBUG) { - Slog.e(LOG_TAG, "No interaction connection to window: " - + accessibilityWindowId); - } - return false; - } final long identityToken = Binder.clearCallingIdentity(); try { final int interactionId = mInteractionIdCounter.getAndIncrement(); @@ -1131,7 +1161,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED - | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; + | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED + | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED + | AccessibilityEvent.TYPE_VIEW_SCROLLED; private int mRetrievalAlowingWindowId; diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java index aab189a..1af7015 100644 --- a/services/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/java/com/android/server/accessibility/TouchExplorer.java @@ -26,7 +26,6 @@ import android.content.Context; import android.os.Handler; import android.os.SystemClock; import android.util.Slog; -import android.util.SparseArray; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.WindowManagerPolicy; @@ -73,15 +72,6 @@ public class TouchExplorer implements Explorer { private static final int STATE_DRAGGING = 0x00000002; private static final int STATE_DELEGATING = 0x00000004; - // Human readable symbolic names for the states of the explorer. - private static final SparseArray<String> sStateSymbolicNames = new SparseArray<String>(); - static { - SparseArray<String> symbolicNames = sStateSymbolicNames; - symbolicNames.append(STATE_TOUCH_EXPLORING, "STATE_TOUCH_EXPLORING"); - symbolicNames.append(STATE_DRAGGING, "STATE_DRAGING"); - symbolicNames.append(STATE_DELEGATING, "STATE_DELEGATING"); - } - // Invalid pointer ID. private static final int INVALID_POINTER_ID = -1; @@ -189,7 +179,7 @@ public class TouchExplorer implements Explorer { if (DEBUG) { Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x" + Integer.toHexString(policyFlags)); - Slog.d(LOG_TAG_STATE, sStateSymbolicNames.get(mCurrentState)); + Slog.d(LOG_TAG_STATE, getStateSymbolicName(mCurrentState)); } // Keep track of the pointers's state. @@ -708,8 +698,7 @@ public class TouchExplorer implements Explorer { private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) { final PointerProperties[] pointerProperties = mTempPointerProperties; final PointerCoords[] pointerCoords = mTempPointerCoords; - final int pointerId = mPointerTracker.getLastReceivedUpPointerId(); - final int pointerIndex = prototype.findPointerIndex(pointerId); + final int pointerIndex = prototype.getActionIndex(); // Send down. prototype.getPointerProperties(pointerIndex, pointerProperties[0]); @@ -884,6 +873,25 @@ public class TouchExplorer implements Explorer { } /** + * Gets the symbolic name of a state. + * + * @param state A state. + * @return The state symbolic name. + */ + private static String getStateSymbolicName(int state) { + switch (state) { + case STATE_TOUCH_EXPLORING: + return "STATE_TOUCH_EXPLORING"; + case STATE_DRAGGING: + return "STATE_DRAGGING"; + case STATE_DELEGATING: + return "STATE_DELEGATING"; + default: + throw new IllegalArgumentException("Unknown state: " + state); + } + } + + /** * Helper class for tracking pointers and more specifically which of * them are currently down, which are active, and which are delivered * to the view hierarchy. The enclosing {@link TouchExplorer} uses the diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4ec71c1..d5e8730 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -502,15 +502,6 @@ public final class ActivityManagerService extends ActivityManagerNative = new ArrayList<ProcessRecord>(); /** - * List of records for processes that we have started and are waiting - * for them to call back. This is really only needed when running in - * single processes mode, in which case we do not have a unique pid for - * each process. - */ - final ArrayList<ProcessRecord> mStartingProcesses - = new ArrayList<ProcessRecord>(); - - /** * List of persistent applications that are in the process * of being started. */ @@ -622,6 +613,11 @@ public final class ActivityManagerService extends ActivityManagerNative } return true; } + + @Override + protected String packageForFilter(BroadcastFilter filter) { + return filter.packageName; + } }; /** @@ -1825,6 +1821,8 @@ public final class ActivityManagerService extends ActivityManagerNative // We already have the app running, or are waiting for it to // come up (we have a pid but not yet its thread), so keep it. if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app); + // If this is a new package in the process, add the package to the list + app.addPackage(info.packageName); return app; } else { // An application record is attached to a previous process, @@ -1946,11 +1944,15 @@ public final class ActivityManagerService extends ActivityManagerNative if ("1".equals(SystemProperties.get("debug.checkjni"))) { debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; } + if ("1".equals(SystemProperties.get("debug.jni.logging"))) { + debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; + } if ("1".equals(SystemProperties.get("debug.assert"))) { debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } int pid = Process.start("android.app.ActivityThread", - app.processName, uid, uid, gids, debugFlags, null); + app.processName, uid, uid, gids, debugFlags, + app.info.targetSdkVersion, null); BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); synchronized (bs) { if (bs.isOnBattery()) { @@ -1990,12 +1992,7 @@ public final class ActivityManagerService extends ActivityManagerNative } buf.append("}"); Slog.i(TAG, buf.toString()); - if (pid == 0 || pid == MY_PID) { - // Processes are being emulated with threads. - app.pid = MY_PID; - app.removed = false; - mStartingProcesses.add(app); - } else if (pid > 0) { + if (pid > 0) { app.pid = pid; app.removed = false; synchronized (mPidsSelfLocked) { @@ -2278,7 +2275,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - return pir.sendInner(0, fillInIntent, resolvedType, + return pir.sendInner(0, fillInIntent, resolvedType, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues); } @@ -3595,9 +3592,6 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); } - } else if (mStartingProcesses.size() > 0) { - app = mStartingProcesses.remove(0); - app.setPid(pid); } else { app = null; } @@ -4031,8 +4025,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { int callingUid = Binder.getCallingUid(); try { - if (callingUid != 0 && callingUid != Process.SYSTEM_UID && - Process.supportsProcesses()) { + if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { int uid = AppGlobals.getPackageManager() .getPackageUid(packageName); if (uid != Binder.getCallingUid()) { @@ -4162,6 +4155,27 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } + public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) { + if (!(pendingResult instanceof PendingIntentRecord)) { + return false; + } + try { + PendingIntentRecord res = (PendingIntentRecord)pendingResult; + if (res.key.allIntents == null) { + return false; + } + for (int i=0; i<res.key.allIntents.length; i++) { + Intent intent = res.key.allIntents[i]; + if (intent.getPackage() != null && intent.getComponent() != null) { + return false; + } + } + return true; + } catch (ClassCastException e) { + } + return false; + } + public void setProcessLimit(int max) { enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, "setProcessLimit()"); @@ -4270,8 +4284,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // Root, system server and our own process get to do everything. - if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID || - !Process.supportsProcesses()) { + if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; } // If there is a uid that owns whatever is being accessed, it has @@ -4415,7 +4428,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean checkUriPermissionLocked(Uri uri, int uid, int modeFlags) { // Root gets to do everything. - if (uid == 0 || !Process.supportsProcesses()) { + if (uid == 0) { return true; } HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid); @@ -5143,7 +5156,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private void removeTaskProcessesLocked(ActivityRecord root) { + private void cleanUpRemovedTaskLocked(ActivityRecord root, boolean killProcesses) { TaskRecord tr = root.task; Intent baseIntent = new Intent( tr.intent != null ? tr.intent : tr.affinityIntent); @@ -5166,6 +5179,7 @@ public final class ActivityManagerService extends ActivityManagerNative ServiceRecord sr = services.get(i); if (sr.startRequested) { if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) { + Slog.i(TAG, "Stopping service " + sr.shortName + ": remove task"); stopServiceLocked(sr); } else { sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, @@ -5177,26 +5191,28 @@ public final class ActivityManagerService extends ActivityManagerNative } } - // Find any running processes associated with this app. - ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); - SparseArray<ProcessRecord> appProcs - = mProcessNames.getMap().get(component.getPackageName()); - if (appProcs != null) { - for (int i=0; i<appProcs.size(); i++) { - procs.add(appProcs.valueAt(i)); - } - } - - // Kill the running processes. - for (int i=0; i<procs.size(); i++) { - ProcessRecord pr = procs.get(i); - if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - Slog.i(TAG, "Killing " + pr + ": remove task"); - EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid, - pr.processName, pr.setAdj, "remove task"); - Process.killProcessQuiet(pr.pid); - } else { - pr.waitingToKill = "remove task"; + if (killProcesses) { + // Find any running processes associated with this app. + ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); + SparseArray<ProcessRecord> appProcs + = mProcessNames.getMap().get(component.getPackageName()); + if (appProcs != null) { + for (int i=0; i<appProcs.size(); i++) { + procs.add(appProcs.valueAt(i)); + } + } + + // Kill the running processes. + for (int i=0; i<procs.size(); i++) { + ProcessRecord pr = procs.get(i); + if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { + Slog.i(TAG, "Killing " + pr.toShortString() + ": remove task"); + EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid, + pr.processName, pr.setAdj, "remove task"); + Process.killProcessQuiet(pr.pid); + } else { + pr.waitingToKill = "remove task"; + } } } } @@ -5210,11 +5226,8 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1); if (r != null) { mRecentTasks.remove(r.task); - - if ((flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0) { - removeTaskProcessesLocked(r); - } - + cleanUpRemovedTaskLocked(r, + (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0); return true; } } finally { @@ -5496,8 +5509,8 @@ public final class ActivityManagerService extends ActivityManagerNative // CONTENT PROVIDERS // ========================================================= - private final List generateApplicationProvidersLocked(ProcessRecord app) { - List providers = null; + private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) { + List<ProviderInfo> providers = null; try { providers = AppGlobals.getPackageManager(). queryContentProviders(app.processName, app.info.uid, @@ -5935,7 +5948,7 @@ public final class ActivityManagerService extends ActivityManagerNative } public static final void installSystemProviders() { - List providers; + List<ProviderInfo> providers; synchronized (mSelf) { ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID); providers = mSelf.generateApplicationProvidersLocked(app); @@ -6553,13 +6566,6 @@ public final class ActivityManagerService extends ActivityManagerNative } public void systemReady(final Runnable goingCallback) { - // In the simulator, startRunning will never have been called, which - // normally sets a few crucial variables. Do it here instead. - if (!Process.supportsProcesses()) { - mStartRunning = true; - mTopAction = Intent.ACTION_MAIN; - } - synchronized(this) { if (mSystemReady) { if (goingCallback != null) goingCallback.run(); @@ -7923,14 +7929,6 @@ public final class ActivityManagerService extends ActivityManagerNative "Starting Norm", "Restarting PERS"); } - if (mStartingProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are starting:"); - dumpProcessList(pw, this, mStartingProcesses, " ", - "Starting Norm", "Starting PERS"); - } - if (mRemovedProcesses.size() > 0) { if (needSep) pw.println(" "); needSep = true; @@ -9895,6 +9893,7 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid); if (app != null && app.thread != null) { try { + app.addPackage(r.appInfo.packageName); realStartServiceLocked(r, app); return true; } catch (RemoteException e) { @@ -10945,7 +10944,7 @@ public final class ActivityManagerService extends ActivityManagerNative mBroadcastsScheduled = true; } - public Intent registerReceiver(IApplicationThread caller, + public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission) { synchronized(this) { ProcessRecord callerApp = null; @@ -10957,6 +10956,13 @@ public final class ActivityManagerService extends ActivityManagerNative + " (pid=" + Binder.getCallingPid() + ") when registering receiver " + receiver); } + if (callerApp.info.uid != Process.SYSTEM_UID && + !callerApp.pkgList.contains(callerPackage)) { + throw new SecurityException("Given caller package " + callerPackage + + " is not running in process " + callerApp); + } + } else { + callerPackage = null; } List allSticky = null; @@ -11001,7 +11007,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mRegisteredReceivers.put(receiver.asBinder(), rl); } - BroadcastFilter bf = new BroadcastFilter(filter, rl, permission); + BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission); rl.add(bf); if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadast"); @@ -12155,6 +12161,7 @@ public final class ActivityManagerService extends ActivityManagerNative info.activityInfo.applicationInfo.uid); if (app != null && app.thread != null) { try { + app.addPackage(info.activityInfo.packageName); processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { @@ -13087,74 +13094,72 @@ public final class ActivityManagerService extends ActivityManagerNative int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); - if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) { - if (app.curRawAdj != app.setRawAdj) { - if (app.curRawAdj > FOREGROUND_APP_ADJ - && app.setRawAdj <= FOREGROUND_APP_ADJ) { - // If this app is transitioning from foreground to - // non-foreground, have it do a gc. - scheduleAppGcLocked(app); - } else if (app.curRawAdj >= HIDDEN_APP_MIN_ADJ - && app.setRawAdj < HIDDEN_APP_MIN_ADJ) { - // Likewise do a gc when an app is moving in to the - // background (such as a service stopping). - scheduleAppGcLocked(app); - } - - if (wasKeeping && !app.keeping) { - // This app is no longer something we want to keep. Note - // its current wake lock time to later know to kill it if - // it is not behaving well. - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - synchronized (stats) { - app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, - app.pid, SystemClock.elapsedRealtime()); - } - app.lastCpuTime = app.curCpuTime; + if (app.curRawAdj != app.setRawAdj) { + if (app.curRawAdj > FOREGROUND_APP_ADJ + && app.setRawAdj <= FOREGROUND_APP_ADJ) { + // If this app is transitioning from foreground to + // non-foreground, have it do a gc. + scheduleAppGcLocked(app); + } else if (app.curRawAdj >= HIDDEN_APP_MIN_ADJ + && app.setRawAdj < HIDDEN_APP_MIN_ADJ) { + // Likewise do a gc when an app is moving in to the + // background (such as a service stopping). + scheduleAppGcLocked(app); + } + + if (wasKeeping && !app.keeping) { + // This app is no longer something we want to keep. Note + // its current wake lock time to later know to kill it if + // it is not behaving well. + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, + app.pid, SystemClock.elapsedRealtime()); } + app.lastCpuTime = app.curCpuTime; + } - app.setRawAdj = app.curRawAdj; + app.setRawAdj = app.curRawAdj; + } + if (adj != app.setAdj) { + if (Process.setOomAdj(app.pid, adj)) { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( + TAG, "Set app " + app.processName + + " oom adj to " + adj); + app.setAdj = adj; + } else { + success = false; } - if (adj != app.setAdj) { - if (Process.setOomAdj(app.pid, adj)) { - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( - TAG, "Set app " + app.processName + - " oom adj to " + adj); - app.setAdj = adj; - } else { - success = false; + } + if (app.setSchedGroup != app.curSchedGroup) { + app.setSchedGroup = app.curSchedGroup; + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, + "Setting process group of " + app.processName + + " to " + app.curSchedGroup); + if (app.waitingToKill != null && + app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { + Slog.i(TAG, "Killing " + app.toShortString() + ": " + app.waitingToKill); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, app.waitingToKill); + Process.killProcessQuiet(app.pid); + } else { + if (true) { + long oldId = Binder.clearCallingIdentity(); + try { + Process.setProcessGroup(app.pid, app.curSchedGroup); + } catch (Exception e) { + Slog.w(TAG, "Failed setting process group of " + app.pid + + " to " + app.curSchedGroup); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldId); + } } - } - if (app.setSchedGroup != app.curSchedGroup) { - app.setSchedGroup = app.curSchedGroup; - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, - "Setting process group of " + app.processName - + " to " + app.curSchedGroup); - if (app.waitingToKill != null && - app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - Slog.i(TAG, "Killing " + app + ": " + app.waitingToKill); - EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, - app.processName, app.setAdj, app.waitingToKill); - Process.killProcessQuiet(app.pid); - } else { - if (true) { - long oldId = Binder.clearCallingIdentity(); + if (false) { + if (app.thread != null) { try { - Process.setProcessGroup(app.pid, app.curSchedGroup); - } catch (Exception e) { - Slog.w(TAG, "Failed setting process group of " + app.pid - + " to " + app.curSchedGroup); - e.printStackTrace(); - } finally { - Binder.restoreCallingIdentity(oldId); - } - } - if (false) { - if (app.thread != null) { - try { - app.thread.setSchedulingGroup(app.curSchedGroup); - } catch (RemoteException e) { - } + app.thread.setSchedulingGroup(app.curSchedGroup); + } catch (RemoteException e) { } } } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index b94ee58..b1da69f 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -652,6 +652,7 @@ final class ActivityStack { if (app != null && app.thread != null) { try { + app.addPackage(r.info.packageName); realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index b4fdc9f..293702d 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -446,6 +446,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub { Binder.getCallingPid(), Binder.getCallingUid(), null); } + private void dumpHelp(PrintWriter pw) { + pw.println("Battery stats (batteryinfo) dump options:"); + pw.println(" [--checkin] [--reset] [--write] [-h]"); + pw.println(" --checkin: format output for a checkin report."); + pw.println(" --reset: reset the stats, clearing all current data."); + pw.println(" --write: force write current collected stats to disk."); + pw.println(" -h: print this help text."); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { boolean isCheckin = false; @@ -466,8 +475,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub { pw.println("Battery stats written."); noOutput = true; } + } else if ("-h".equals(arg)) { + dumpHelp(pw); + return; } else { pw.println("Unknown option: " + arg); + dumpHelp(pw); } } } diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java index 2e784d3..b49bc22 100644 --- a/services/java/com/android/server/am/BroadcastFilter.java +++ b/services/java/com/android/server/am/BroadcastFilter.java @@ -25,12 +25,14 @@ import java.io.PrintWriter; class BroadcastFilter extends IntentFilter { // Back-pointer to the list this filter is in. final ReceiverList receiverList; + final String packageName; final String requiredPermission; BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList, - String _requiredPermission) { + String _packageName, String _requiredPermission) { super(_filter); receiverList = _receiverList; + packageName = _packageName; requiredPermission = _requiredPermission; } diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index ee6e420..8ed0cc1 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -177,13 +177,13 @@ class PendingIntentRecord extends IIntentSender.Stub { } public int send(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver) { + IIntentReceiver finishedReceiver, String requiredPermission) { return sendInner(code, intent, resolvedType, finishedReceiver, - null, null, 0, 0, 0); + requiredPermission, null, null, 0, 0, 0); } int sendInner(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver, + IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues) { synchronized(owner) { @@ -246,8 +246,8 @@ class PendingIntentRecord extends IIntentSender.Stub { // that the broadcast be delivered synchronously owner.broadcastIntentInPackage(key.packageName, uid, finalIntent, resolvedType, - finishedReceiver, code, null, null, null, - (finishedReceiver != null), false); + finishedReceiver, code, null, null, + requiredPermission, (finishedReceiver != null), false); sendFinish = false; } catch (RuntimeException e) { Slog.w(ActivityManagerService.TAG, diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 911cac2..6bb7949 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -189,8 +189,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mDnsServers[1] = DNS_DEFAULT_SERVER2; } - public void interfaceLinkStatusChanged(String iface, boolean link) { - if (DEBUG) Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link); + public void interfaceStatusChanged(String iface, boolean up) { + if (DEBUG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up); boolean found = false; boolean usb = false; if (isWifi(iface)) { @@ -205,7 +205,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { synchronized (mIfaces) { TetherInterfaceSM sm = mIfaces.get(iface); - if (link) { + if (up) { if (sm == null) { sm = new TetherInterfaceSM(iface, mLooper, usb); mIfaces.put(iface, sm); @@ -220,6 +220,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } + public void interfaceLinkStateChanged(String iface, boolean up) { + if (DEBUG) Log.d(TAG, "interfaceLinkStateChanged " + iface + ", " + up); + interfaceStatusChanged(iface, up); + } + private boolean isUsb(String iface) { for (String regex : mTetherableUsbRegexs) { if (iface.matches(regex)) return true; @@ -689,6 +694,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub { return retVal; } + //TODO: Temporary handling upstream change triggered without + // CONNECTIVITY_ACTION. Only to accomodate interface + // switch during HO. + // @see bug/4455071 + public void handleTetherIfaceChange() { + mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED); + } + class TetherInterfaceSM extends StateMachine { // notification from the master SM that it's not in tether mode static final int CMD_TETHER_MODE_DEAD = 1; diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index 47813f8..c185012 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -27,15 +27,24 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.INetworkManagementEventObserver; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; import android.os.Binder; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; +import android.os.Process; +import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Log; import com.android.internal.R; +import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.server.ConnectivityService.VpnCallback; +import java.io.OutputStream; +import java.nio.charset.Charsets; +import java.util.Arrays; + /** * @hide */ @@ -47,9 +56,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private final Context mContext; private final VpnCallback mCallback; - private String mPackageName; - private String mInterfaceName; - private String mDnsPropertyPrefix; + private String mPackage = VpnConfig.LEGACY_VPN; + private String mInterface; + private LegacyVpnRunner mLegacyVpnRunner; public Vpn(Context context, VpnCallback callback) { mContext = context; @@ -57,74 +66,98 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } /** - * Prepare for a VPN application. + * Protect a socket from routing changes by binding it to the given + * interface. The socket IS closed by this method. + * + * @param socket The socket to be bound. + * @param name The name of the interface. + */ + public void protect(ParcelFileDescriptor socket, String interfaze) { + try { + mContext.enforceCallingPermission(VPN, "protect"); + jniProtect(socket.getFd(), interfaze); + } finally { + try { + socket.close(); + } catch (Exception e) { + // ignore + } + } + } + + /** + * Prepare for a VPN application. This method is designed to solve + * race conditions. It first compares the current prepared package + * with {@code oldPackage}. If they are the same, the prepared + * package is revoked and replaced with {@code newPackage}. If + * {@code oldPackage} is {@code null}, the comparison is omitted. + * If {@code newPackage} is the same package or {@code null}, the + * revocation is omitted. This method returns {@code true} if the + * operation is succeeded. + * + * Legacy VPN is handled specially since it is not a real package. + * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and + * it can be revoked by itself. * - * @param packageName The package name of the new VPN application. - * @return The name of the current prepared package. + * @param oldPackage The package name of the old VPN application. + * @param newPackage The package name of the new VPN application. + * @return true if the operation is succeeded. */ - public synchronized String prepare(String packageName) { - // Return the current prepared package if the new one is null. - if (packageName == null) { - return mPackageName; + public synchronized boolean prepare(String oldPackage, String newPackage) { + // Return false if the package does not match. + if (oldPackage != null && !oldPackage.equals(mPackage)) { + return false; } - // Check the permission of the caller. - PackageManager pm = mContext.getPackageManager(); - VpnConfig.enforceCallingPackage(pm.getNameForUid(Binder.getCallingUid())); + // Return true if we do not need to revoke. + if (newPackage == null || + (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) { + return true; + } + + // Only system user can revoke a package. + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Unauthorized Caller"); + } // Check the permission of the given package. - if (packageName.isEmpty()) { - packageName = null; - } else if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException(packageName + " does not have " + VPN); + PackageManager pm = mContext.getPackageManager(); + if (!newPackage.equals(VpnConfig.LEGACY_VPN) && + pm.checkPermission(VPN, newPackage) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(newPackage + " does not have " + VPN); } // Reset the interface and hide the notification. - if (mInterfaceName != null) { - nativeReset(mInterfaceName); + if (mInterface != null) { + jniReset(mInterface); mCallback.restore(); hideNotification(); - mInterfaceName = null; + mInterface = null; } - // Notify the package being revoked. - if (mPackageName != null) { + // Send out the broadcast or stop LegacyVpnRunner. + if (!mPackage.equals(VpnConfig.LEGACY_VPN)) { Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED); - intent.setPackage(mPackageName); + intent.setPackage(mPackage); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcast(intent); + } else if (mLegacyVpnRunner != null) { + mLegacyVpnRunner.exit(); + mLegacyVpnRunner = null; } - Log.i(TAG, "Switched from " + mPackageName + " to " + packageName); - mPackageName = packageName; - return mPackageName; - } - - /** - * Protect a socket from routing changes by binding it to the given - * interface. The socket IS closed by this method. - * - * @param socket The socket to be bound. - * @param name The name of the interface. - */ - public void protect(ParcelFileDescriptor socket, String name) { - try { - mContext.enforceCallingPermission(VPN, "protect"); - nativeProtect(socket.getFd(), name); - } finally { - try { - socket.close(); - } catch (Exception e) { - // ignore - } - } + Log.i(TAG, "Switched from " + mPackage + " to " + newPackage); + mPackage = newPackage; + return true; } /** - * Configure a TUN interface and return its file descriptor. + * Establish a VPN network and return the file descriptor of the VPN + * interface. This methods returns {@code null} if the application is + * revoked or not prepared. * - * @param configuration The parameters to configure the interface. - * @return The file descriptor of the interface. + * @param config The parameters to configure the network. + * @return The file descriptor of the VPN interface. */ public synchronized ParcelFileDescriptor establish(VpnConfig config) { // Check the permission of the caller. @@ -134,7 +167,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { PackageManager pm = mContext.getPackageManager(); ApplicationInfo app = null; try { - app = pm.getApplicationInfo(mPackageName, 0); + app = pm.getApplicationInfo(mPackage, 0); } catch (Exception e) { return null; } @@ -142,85 +175,91 @@ public class Vpn extends INetworkManagementEventObserver.Stub { return null; } - // Create and configure the interface. - ParcelFileDescriptor descriptor = ParcelFileDescriptor.adoptFd( - nativeEstablish(config.mtu, config.addresses, config.routes)); + // Load the label. + String label = app.loadLabel(pm).toString(); + + // Load the icon and convert it into a bitmap. + Drawable icon = app.loadIcon(pm); + Bitmap bitmap = null; + if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) { + int width = mContext.getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_width); + int height = mContext.getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_height); + icon.setBounds(0, 0, width, height); + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + icon.draw(new Canvas(bitmap)); + } - // Replace the interface and abort if it fails. + // Configure the interface. Abort if any of these steps fails. + ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd( + jniConfigure(config.mtu, config.addresses, config.routes)); try { - String interfaceName = nativeGetName(descriptor.getFd()); - - if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) { - nativeReset(mInterfaceName); + String interfaze = jniGetName(tun.getFd()); + if (mInterface != null && !mInterface.equals(interfaze)) { + jniReset(mInterface); } - mInterfaceName = interfaceName; + mInterface = interfaze; } catch (RuntimeException e) { try { - descriptor.close(); + tun.close(); } catch (Exception ex) { // ignore } throw e; } - String dnsServers = (config.dnsServers == null) ? "" : config.dnsServers.trim(); - mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" ")); + // Override DNS servers and search domains. + mCallback.override(config.dnsServers, config.searchDomains); + + // Fill more values. + config.packagz = mPackage; + config.interfaze = mInterface; - config.packageName = mPackageName; - config.interfaceName = mInterfaceName; - showNotification(pm, app, config); - return descriptor; + // Show the notification! + showNotification(config, label, bitmap); + return tun; } // INetworkManagementEventObserver.Stub - public void interfaceLinkStatusChanged(String name, boolean up) { + public void interfaceStatusChanged(String interfaze, boolean up) { } // INetworkManagementEventObserver.Stub - public void interfaceAdded(String name) { + public void interfaceLinkStateChanged(String interfaze, boolean up) { } // INetworkManagementEventObserver.Stub - public synchronized void interfaceRemoved(String name) { - if (name.equals(mInterfaceName) && nativeCheck(name) == 0) { - hideNotification(); - mInterfaceName = null; + public void interfaceAdded(String interfaze) { + } + + // INetworkManagementEventObserver.Stub + public synchronized void interfaceRemoved(String interfaze) { + if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { mCallback.restore(); + hideNotification(); + mInterface = null; } } - private void showNotification(PackageManager pm, ApplicationInfo app, VpnConfig config) { + private void showNotification(VpnConfig config, String label, Bitmap icon) { NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (nm != null) { - // Load the icon and convert it into a bitmap. - Drawable icon = app.loadIcon(pm); - Bitmap bitmap = null; - if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) { - int width = mContext.getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_width); - int height = mContext.getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_height); - icon.setBounds(0, 0, width, height); - bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - icon.draw(new Canvas(bitmap)); - } + String title = (label == null) ? mContext.getString(R.string.vpn_title) : + mContext.getString(R.string.vpn_title_long, label); + String text = (config.session == null) ? mContext.getString(R.string.vpn_text) : + mContext.getString(R.string.vpn_text_long, config.session); + config.startTime = SystemClock.elapsedRealtime(); - // Load the label. - String label = app.loadLabel(pm).toString(); - - // Build the notification. - String text = (config.sessionName == null) ? mContext.getString(R.string.vpn_text) : - mContext.getString(R.string.vpn_text_long, config.sessionName); long identity = Binder.clearCallingIdentity(); Notification notification = new Notification.Builder(mContext) .setSmallIcon(R.drawable.vpn_connected) - .setLargeIcon(bitmap) - .setTicker(mContext.getString(R.string.vpn_ticker, label)) - .setContentTitle(mContext.getString(R.string.vpn_title, label)) + .setLargeIcon(icon) + .setContentTitle(title) .setContentText(text) - .setContentIntent(VpnConfig.getIntentForNotification(mContext, config)) + .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config)) .setDefaults(Notification.DEFAULT_ALL) .setOngoing(true) .getNotification(); @@ -240,9 +279,264 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } } - private native int nativeEstablish(int mtu, String addresses, String routes); - private native String nativeGetName(int fd); - private native void nativeReset(String name); - private native int nativeCheck(String name); - private native void nativeProtect(int fd, String name); + private native int jniConfigure(int mtu, String addresses, String routes); + private native String jniGetName(int tun); + private native void jniReset(String interfaze); + private native int jniCheck(String interfaze); + private native void jniProtect(int socket, String interfaze); + + /** + * Start legacy VPN. This method stops the daemons and restart them + * if arguments are not null. Heavy things are offloaded to another + * thread, so callers will not be blocked for a long time. + * + * @param config The parameters to configure the network. + * @param raoocn The arguments to be passed to racoon. + * @param mtpd The arguments to be passed to mtpd. + */ + public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { + // Prepare for the new request. This also checks the caller. + prepare(null, VpnConfig.LEGACY_VPN); + + // Start a new LegacyVpnRunner and we are done! + mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd); + mLegacyVpnRunner.start(); + } + + /** + * Return the information of the current ongoing legacy VPN. + */ + public synchronized LegacyVpnInfo getLegacyVpnInfo() { + // Only system user can call this method. + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Unauthorized Caller"); + } + return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo(); + } + + /** + * Bringing up a VPN connection takes time, and that is all this thread + * does. Here we have plenty of time. The only thing we need to take + * care of is responding to interruptions as soon as possible. Otherwise + * requests will be piled up. This can be done in a Handler as a state + * machine, but it is much easier to read in the current form. + */ + private class LegacyVpnRunner extends Thread { + private static final String TAG = "LegacyVpnRunner"; + private static final String NONE = "--"; + + private final VpnConfig mConfig; + private final String[] mDaemons; + private final String[][] mArguments; + private final LegacyVpnInfo mInfo; + + private long mTimer = -1; + + public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) { + super(TAG); + mConfig = config; + mDaemons = new String[] {"racoon", "mtpd"}; + mArguments = new String[][] {racoon, mtpd}; + mInfo = new LegacyVpnInfo(); + + // Legacy VPN is not a real package, so we use it to carry the key. + mInfo.key = mConfig.packagz; + mConfig.packagz = VpnConfig.LEGACY_VPN; + } + + public void exit() { + // We assume that everything is reset after the daemons die. + for (String daemon : mDaemons) { + SystemProperties.set("ctl.stop", daemon); + } + interrupt(); + } + + public LegacyVpnInfo getInfo() { + // Update the info when VPN is disconnected. + if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) { + mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED; + mInfo.intent = null; + } + return mInfo; + } + + @Override + public void run() { + // Wait for the previous thread since it has been interrupted. + Log.v(TAG, "Waiting"); + synchronized (TAG) { + Log.v(TAG, "Executing"); + execute(); + } + } + + private void checkpoint(boolean yield) throws InterruptedException { + long now = SystemClock.elapsedRealtime(); + if (mTimer == -1) { + mTimer = now; + Thread.sleep(1); + } else if (now - mTimer <= 30000) { + Thread.sleep(yield ? 200 : 1); + } else { + mInfo.state = LegacyVpnInfo.STATE_TIMEOUT; + throw new IllegalStateException("time is up"); + } + } + + private void execute() { + // Catch all exceptions so we can clean up few things. + try { + // Initialize the timer. + checkpoint(false); + mInfo.state = LegacyVpnInfo.STATE_INITIALIZING; + + // First stop the daemons. + for (String daemon : mDaemons) { + SystemProperties.set("ctl.stop", daemon); + } + + // Wait for the daemons to stop. + for (String daemon : mDaemons) { + String key = "init.svc." + daemon; + while (!"stopped".equals(SystemProperties.get(key))) { + checkpoint(true); + } + } + + // Reset the properties. + SystemProperties.set("vpn.dns", NONE); + SystemProperties.set("vpn.via", NONE); + while (!NONE.equals(SystemProperties.get("vpn.dns")) || + !NONE.equals(SystemProperties.get("vpn.via"))) { + checkpoint(true); + } + + // Check if we need to restart any of the daemons. + boolean restart = false; + for (String[] arguments : mArguments) { + restart = restart || (arguments != null); + } + if (!restart) { + mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED; + return; + } + mInfo.state = LegacyVpnInfo.STATE_CONNECTING; + + // Start the daemon with arguments. + for (int i = 0; i < mDaemons.length; ++i) { + String[] arguments = mArguments[i]; + if (arguments == null) { + continue; + } + + // Start the daemon. + String daemon = mDaemons[i]; + SystemProperties.set("ctl.start", daemon); + + // Wait for the daemon to start. + String key = "init.svc." + daemon; + while (!"running".equals(SystemProperties.get(key))) { + checkpoint(true); + } + + // Create the control socket. + LocalSocket socket = new LocalSocket(); + LocalSocketAddress address = new LocalSocketAddress( + daemon, LocalSocketAddress.Namespace.RESERVED); + + // Wait for the socket to connect. + while (true) { + try { + socket.connect(address); + break; + } catch (Exception e) { + // ignore + } + checkpoint(true); + } + socket.setSoTimeout(500); + + // Send over the arguments. + OutputStream out = socket.getOutputStream(); + for (String argument : arguments) { + byte[] bytes = argument.getBytes(Charsets.UTF_8); + if (bytes.length >= 0xFFFF) { + throw new IllegalArgumentException("argument is too large"); + } + out.write(bytes.length >> 8); + out.write(bytes.length); + out.write(bytes); + checkpoint(false); + } + + // Send End-Of-Arguments. + out.write(0xFF); + out.write(0xFF); + out.flush(); + socket.close(); + } + + // Now here is the beast from the old days. We check few + // properties to figure out the current status. Ideally we + // can read things back from the sockets and get rid of the + // properties, but we have no time... + while (NONE.equals(SystemProperties.get("vpn.dns")) || + NONE.equals(SystemProperties.get("vpn.via"))) { + + // Check if a running daemon is dead. + for (int i = 0; i < mDaemons.length; ++i) { + String daemon = mDaemons[i]; + if (mArguments[i] != null && !"running".equals( + SystemProperties.get("init.svc." + daemon))) { + throw new IllegalStateException(daemon + " is dead"); + } + } + checkpoint(true); + } + + // Now we are connected. Get the interface. + mConfig.interfaze = SystemProperties.get("vpn.via"); + + // Get the DNS servers if they are not set in config. + if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) { + String dnsServers = SystemProperties.get("vpn.dns").trim(); + if (!dnsServers.isEmpty()) { + mConfig.dnsServers = Arrays.asList(dnsServers.split(" ")); + } + } + + // TODO: support search domains from ISAKMP mode config. + + // The final step must be synchronized. + synchronized (Vpn.this) { + // Check if the thread is interrupted while we are waiting. + checkpoint(false); + + // Check if the interface is gone while we are waiting. + if (jniCheck(mConfig.interfaze) == 0) { + throw new IllegalStateException(mConfig.interfaze + " is gone"); + } + + // Now INetworkManagementEventObserver is watching our back. + mInterface = mConfig.interfaze; + mCallback.override(mConfig.dnsServers, mConfig.searchDomains); + showNotification(mConfig, null, null); + + Log.i(TAG, "Connected!"); + mInfo.state = LegacyVpnInfo.STATE_CONNECTED; + mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null); + } + } catch (Exception e) { + Log.i(TAG, "Aborting", e); + exit(); + } finally { + // Do not leave an unstable state. + if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING || + mInfo.state == LegacyVpnInfo.STATE_CONNECTING) { + mInfo.state = LegacyVpnInfo.STATE_FAILED; + } + } + } + } } diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 67e73f5..c813d37 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -32,7 +32,6 @@ import android.location.LocationManager; import android.location.LocationProvider; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.SntpClient; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -51,6 +50,7 @@ import android.telephony.SmsMessage; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.util.Log; +import android.util.NtpTrustedTime; import android.util.SparseIntArray; import com.android.internal.app.IBatteryStats; @@ -61,7 +61,7 @@ import com.android.internal.telephony.Phone; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.StringBufferInputStream; +import java.io.StringReader; import java.util.ArrayList; import java.util.Date; import java.util.Map.Entry; @@ -132,7 +132,7 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final int GPS_CAPABILITY_MSB = 0x0000002; private static final int GPS_CAPABILITY_MSA = 0x0000004; private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008; - + private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010; // these need to match AGpsType enum in gps.h private static final int AGPS_TYPE_SUPL = 1; @@ -200,6 +200,9 @@ public class GpsLocationProvider implements LocationProviderInterface { private boolean mInjectNtpTimePending = true; private boolean mDownloadXtraDataPending = true; + // set to true if the GPS engine does not do on-demand NTP time requests + private boolean mPeriodicTimeInjection; + // true if GPS is navigating private boolean mNavigating; @@ -232,13 +235,13 @@ public class GpsLocationProvider implements LocationProviderInterface { // properties loaded from PROPERTIES_FILE private Properties mProperties; - private String mNtpServer; private String mSuplServerHost; private int mSuplServerPort; private String mC2KServerHost; private int mC2KServerPort; private final Context mContext; + private final NtpTrustedTime mNtpTime; private final ILocationManager mLocationManager; private Location mLocation = new Location(LocationManager.GPS_PROVIDER); private Bundle mLocationExtras = new Bundle(); @@ -283,10 +286,6 @@ public class GpsLocationProvider implements LocationProviderInterface { // current setting - 5 minutes private static final long RETRY_INTERVAL = 5*60*1000; - // to avoid injecting bad NTP time, we reject any time fixes that differ from system time - // by more than 5 minutes. - private static final long MAX_NTP_SYSTEM_TIME_OFFSET = 5*60*1000; - private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() { public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException { if (listener == null) { @@ -375,6 +374,7 @@ public class GpsLocationProvider implements LocationProviderInterface { public GpsLocationProvider(Context context, ILocationManager locationManager) { mContext = context; + mNtpTime = NtpTrustedTime.getInstance(context); mLocationManager = locationManager; mNIHandler = new GpsNetInitiatedHandler(context); @@ -415,7 +415,6 @@ public class GpsLocationProvider implements LocationProviderInterface { FileInputStream stream = new FileInputStream(file); mProperties.load(stream); stream.close(); - mNtpServer = mProperties.getProperty("NTP_SERVER", null); mSuplServerHost = mProperties.getProperty("SUPL_HOST"); String portString = mProperties.getProperty("SUPL_PORT"); @@ -527,13 +526,18 @@ public class GpsLocationProvider implements LocationProviderInterface { } mInjectNtpTimePending = false; - SntpClient client = new SntpClient(); long delay; - if (client.requestTime(mNtpServer, 10000)) { - long time = client.getNtpTime(); - long timeReference = client.getNtpTimeReference(); - int certainty = (int)(client.getRoundTripTime()/2); + // force refresh NTP cache when outdated + if (mNtpTime.getCacheAge() >= NTP_INTERVAL) { + mNtpTime.forceRefresh(); + } + + // only update when NTP time is fresh + if (mNtpTime.getCacheAge() < NTP_INTERVAL) { + long time = mNtpTime.getCachedNtpTime(); + long timeReference = mNtpTime.getCachedNtpTimeReference(); + long certainty = mNtpTime.getCacheCertainty(); long now = System.currentTimeMillis(); Log.d(TAG, "NTP server returned: " @@ -542,17 +546,19 @@ public class GpsLocationProvider implements LocationProviderInterface { + " certainty: " + certainty + " system time offset: " + (time - now)); - native_inject_time(time, timeReference, certainty); + native_inject_time(time, timeReference, (int) certainty); delay = NTP_INTERVAL; } else { if (DEBUG) Log.d(TAG, "requestTime failed"); delay = RETRY_INTERVAL; } - // send delayed message for next NTP injection - // since this is delayed and not urgent we do not hold a wake lock here - mHandler.removeMessages(INJECT_NTP_TIME); - mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); + if (mPeriodicTimeInjection) { + // send delayed message for next NTP injection + // since this is delayed and not urgent we do not hold a wake lock here + mHandler.removeMessages(INJECT_NTP_TIME); + mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); + } } private void handleDownloadXtraData() { @@ -1305,6 +1311,11 @@ public class GpsLocationProvider implements LocationProviderInterface { */ private void setEngineCapabilities(int capabilities) { mEngineCapabilities = capabilities; + + if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) { + mPeriodicTimeInjection = true; + requestUtcTime(); + } } /** @@ -1385,7 +1396,7 @@ public class GpsLocationProvider implements LocationProviderInterface { Properties extraProp = new Properties(); try { - extraProp.load(new StringBufferInputStream(extras)); + extraProp.load(new StringReader(extras)); } catch (IOException e) { @@ -1438,6 +1449,14 @@ public class GpsLocationProvider implements LocationProviderInterface { } /** + * Called from native code to request utc time info + */ + + private void requestUtcTime() { + sendMessage(INJECT_NTP_TIME, 0, null); + } + + /** * Called from native code to request reference location info */ diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 584cd03..d23d0f4 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_PHONE_STATE; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.*; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -57,6 +58,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IConnectivityManager; @@ -71,7 +74,9 @@ import android.net.NetworkTemplate; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.INetworkManagementService; import android.os.IPowerManager; +import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.TelephonyManager; @@ -108,6 +113,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import libcore.io.IoUtils; @@ -148,10 +154,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; + private static final int MSG_RULES_CHANGED = 0x1; + private static final int MSG_METERED_IFACES_CHANGED = 0x2; + private final Context mContext; private final IActivityManager mActivityManager; private final IPowerManager mPowerManager; private final INetworkStatsService mNetworkStats; + private final INetworkManagementService mNetworkManagement; private final TrustedTime mTime; private IConnectivityManager mConnManager; @@ -160,6 +170,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final Object mRulesLock = new Object(); private boolean mScreenOn; + private boolean mBackgroundData; /** Current policy for network templates. */ private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList(); @@ -188,11 +199,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: keep whitelist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. + // TODO: watch for package added broadcast to catch new UIDs. + public NetworkPolicyManagerService(Context context, IActivityManager activityManager, - IPowerManager powerManager, INetworkStatsService networkStats) { - // TODO: move to using cached NtpTrustedTime - this(context, activityManager, powerManager, networkStats, new NtpTrustedTime(), - getSystemDir()); + IPowerManager powerManager, INetworkStatsService networkStats, + INetworkManagementService networkManagement) { + this(context, activityManager, powerManager, networkStats, networkManagement, + NtpTrustedTime.getInstance(context), getSystemDir()); } private static File getSystemDir() { @@ -200,17 +213,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } public NetworkPolicyManagerService(Context context, IActivityManager activityManager, - IPowerManager powerManager, INetworkStatsService networkStats, TrustedTime time, - File systemDir) { + IPowerManager powerManager, INetworkStatsService networkStats, + INetworkManagementService networkManagement, + TrustedTime time, File systemDir) { mContext = checkNotNull(context, "missing context"); mActivityManager = checkNotNull(activityManager, "missing activityManager"); mPowerManager = checkNotNull(powerManager, "missing powerManager"); mNetworkStats = checkNotNull(networkStats, "missing networkStats"); + mNetworkManagement = checkNotNull(networkManagement, "missing networkManagement"); mTime = checkNotNull(time, "missing TrustedTime"); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); + mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback); mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml")); } @@ -231,6 +246,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } updateScreenOn(); + updateBackgroundData(true); try { mActivityManager.registerProcessObserver(mProcessObserver); @@ -256,11 +272,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED); mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler); - // listen for warning polling events; currently dispatched by + // listen for stats update events final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED); mContext.registerReceiver( mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler); + // listen for changes to background data flag + final IntentFilter bgFilter = new IntentFilter(ACTION_BACKGROUND_DATA_SETTING_CHANGED); + mContext.registerReceiver(mBgReceiver, bgFilter, CONNECTIVITY_INTERNAL, mHandler); + } private IProcessObserver mProcessObserver = new IProcessObserver.Stub() { @@ -269,9 +289,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); - // skip when UID couldn't have any policy - if (!isUidValidForPolicy(mContext, uid)) return; - synchronized (mRulesLock) { // because a uid can have multiple pids running inside, we need to // remember all pid states and summarize foreground at uid level. @@ -292,9 +309,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); - // skip when UID couldn't have any policy - if (!isUidValidForPolicy(mContext, uid)) return; - synchronized (mRulesLock) { // clear records and recompute, when they exist final SparseBooleanArray pidForeground = mUidPidForeground.get(uid); @@ -349,6 +363,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** + * Receiver that watches for + * {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}. + */ + private BroadcastReceiver mBgReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and verified CONNECTIVITY_INTERNAL + // permission above. + + synchronized (mRulesLock) { + updateBackgroundData(false); + } + } + }; + + /** * Check {@link NetworkPolicy} against current {@link INetworkStatsService} * to show visible notifications as needed. */ @@ -561,7 +591,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); - mMeteredIfaces.clear(); + final HashSet<String> newMeteredIfaces = Sets.newHashSet(); // apply each policy that we found ifaces for; compute remaining data // based on current cycle and historical stats, and push to kernel. @@ -591,28 +621,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { // remaining "quota" is based on usage in current cycle final long quota = Math.max(0, policy.limitBytes - total); - //kernelSetIfacesQuota(ifaces, quota); + + if (ifaces.length > 1) { + // TODO: switch to shared quota once NMS supports + Slog.w(TAG, "shared quota unsupported; generating rule for each iface"); + } for (String iface : ifaces) { - mMeteredIfaces.add(iface); + removeInterfaceQuota(iface); + setInterfaceQuota(iface, quota); + newMeteredIfaces.add(iface); } } } - // dispatch changed rule to existing listeners - // TODO: dispatch outside of holding lock - final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); - final int length = mListeners.beginBroadcast(); - for (int i = 0; i < length; i++) { - final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); - if (listener != null) { - try { - listener.onMeteredIfacesChanged(meteredIfaces); - } catch (RemoteException e) { - } + // remove quota on any trailing interfaces + for (String iface : mMeteredIfaces) { + if (!newMeteredIfaces.contains(iface)) { + removeInterfaceQuota(iface); } } - mListeners.finishBroadcast(); + mMeteredIfaces = newMeteredIfaces; + + final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); + mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget(); } /** @@ -804,32 +836,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.register(listener); - synchronized (mRulesLock) { - // dispatch any existing rules to new listeners - // TODO: dispatch outside of holding lock - final int size = mUidRules.size(); - for (int i = 0; i < size; i++) { - final int uid = mUidRules.keyAt(i); - final int uidRules = mUidRules.valueAt(i); - if (uidRules != RULE_ALLOW_ALL) { - try { - listener.onUidRulesChanged(uid, uidRules); - } catch (RemoteException e) { - } - } - } - - // dispatch any metered ifaces to new listeners - // TODO: dispatch outside of holding lock - if (mMeteredIfaces.size() > 0) { - final String[] meteredIfaces = mMeteredIfaces.toArray( - new String[mMeteredIfaces.size()]); - try { - listener.onMeteredIfacesChanged(meteredIfaces); - } catch (RemoteException e) { - } - } - } + // TODO: consider dispatching existing rules to new listeners } @Override @@ -963,6 +970,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void updateBackgroundData(boolean systemReady) { + synchronized (mRulesLock) { + try { + mBackgroundData = mConnManager.getBackgroundDataSetting(); + } catch (RemoteException e) { + } + if (systemReady && mBackgroundData) { + // typical behavior of background enabled during systemReady; + // no need to clear rules for all UIDs. + } else { + updateRulesForBackgroundDataLocked(); + } + } + } + /** * Update rules that might be changed by {@link #mScreenOn} value. */ @@ -977,9 +999,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private void updateRulesForUidLocked(int uid) { - if (!isUidValidForPolicy(mContext, uid)) return; + /** + * Update rules that might be changed by {@link #mBackgroundData} value. + */ + private void updateRulesForBackgroundDataLocked() { + // update rules for all installed applications + final PackageManager pm = mContext.getPackageManager(); + final List<ApplicationInfo> apps = pm.getInstalledApplications(0); + for (ApplicationInfo app : apps) { + updateRulesForUidLocked(app.uid); + } + // and catch system UIDs + // TODO: keep in sync with android_filesystem_config.h + for (int uid = 1000; uid <= 1025; uid++) { + updateRulesForUidLocked(uid); + } + for (int uid = 2000; uid <= 2002; uid++) { + updateRulesForUidLocked(uid); + } + for (int uid = 3000; uid <= 3007; uid++) { + updateRulesForUidLocked(uid); + } + for (int uid = 9998; uid <= 9999; uid++) { + updateRulesForUidLocked(uid); + } + } + + private void updateRulesForUidLocked(int uid) { final int uidPolicy = getUidPolicy(uid); final boolean uidForeground = isUidForeground(uid); @@ -989,28 +1036,96 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // uid in background, and policy says to block metered data uidRules = RULE_REJECT_METERED; } + if (!uidForeground && !mBackgroundData) { + // uid in background, and global background disabled + uidRules = RULE_REJECT_METERED; + } // TODO: only dispatch when rules actually change - // record rule locally to dispatch to new listeners - mUidRules.put(uid, uidRules); + if (uidRules == RULE_ALLOW_ALL) { + mUidRules.delete(uid); + } else { + mUidRules.put(uid, uidRules); + } final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0; - //kernelSetUidRejectPaid(uid, rejectPaid); + setUidNetworkRules(uid, rejectMetered); // dispatch changed rule to existing listeners - // TODO: dispatch outside of holding lock - final int length = mListeners.beginBroadcast(); - for (int i = 0; i < length; i++) { - final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); - if (listener != null) { - try { - listener.onUidRulesChanged(uid, uidRules); - } catch (RemoteException e) { + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget(); + } + + private Handler.Callback mHandlerCallback = new Handler.Callback() { + /** {@inheritDoc} */ + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_RULES_CHANGED: { + final int uid = msg.arg1; + final int uidRules = msg.arg2; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + if (listener != null) { + try { + listener.onUidRulesChanged(uid, uidRules); + } catch (RemoteException e) { + } + } + } + mListeners.finishBroadcast(); + return true; + } + case MSG_METERED_IFACES_CHANGED: { + final String[] meteredIfaces = (String[]) msg.obj; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + if (listener != null) { + try { + listener.onMeteredIfacesChanged(meteredIfaces); + } catch (RemoteException e) { + } + } + } + mListeners.finishBroadcast(); + return true; + } + default: { + return false; } } } - mListeners.finishBroadcast(); + }; + + private void setInterfaceQuota(String iface, long quota) { + try { + mNetworkManagement.setInterfaceQuota(iface, quota); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem setting interface quota", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem setting interface quota", e); + } + } + + private void removeInterfaceQuota(String iface) { + try { + mNetworkManagement.removeInterfaceQuota(iface); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem removing interface quota", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem removing interface quota", e); + } + } + + private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { + try { + mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces); + } catch (IllegalStateException e) { + Slog.e(TAG, "problem setting uid rules", e); + } catch (RemoteException e) { + Slog.e(TAG, "problem setting uid rules", e); + } } private String getActiveSubscriberId() { diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 524dd40..b6834f6 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -27,7 +27,6 @@ import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_REMOVED; -import static android.provider.Settings.Secure.NETSTATS_ENABLED; import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY; import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD; @@ -71,7 +70,6 @@ import android.util.Slog; import android.util.TrustedTime; import com.android.internal.os.AtomicFile; -import com.android.server.NativeDaemonConnectorException; import com.google.android.collect.Maps; import com.google.android.collect.Sets; @@ -124,8 +122,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private PendingIntent mPollIntent; // TODO: listen for kernel push events through netd instead of polling - // TODO: watch for UID uninstall, and transfer stats into single bucket - // TODO: trim empty history objects entirely private static final long KB_IN_BYTES = 1024; @@ -136,7 +132,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Settings that can be changed externally. */ public interface NetworkStatsSettings { - public boolean getEnabled(); public long getPollInterval(); public long getPersistThreshold(); public long getNetworkBucketDuration(); @@ -178,9 +173,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsService( Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) { - // TODO: move to using cached NtpTrustedTime - this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir(), - new DefaultNetworkStatsSettings(context)); + this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context), + getSystemDir(), new DefaultNetworkStatsSettings(context)); } private static File getSystemDir() { @@ -209,20 +203,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } public void systemReady() { - if (mSettings.getEnabled()) { - try { - // enable low-level bandwidth stats and control - // TODO: consider shipping with this enabled by default - mNetworkManager.setBandwidthControlEnabled(true); - } catch (RemoteException e) { - Slog.e(TAG, "problem talking to netd while enabling bandwidth controls", e); - } catch (NativeDaemonConnectorException ndce) { - Slog.e(TAG, "problem enabling bandwidth controls", ndce); - } - } else { - Slog.w(TAG, "detailed network stats disabled"); - } - synchronized (mStatsLock) { // read historical network stats from disk, since policy service // might need them right away. we delay loading detailed UID stats @@ -391,6 +371,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + @Override + public void forceUpdate() { + mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); + + synchronized (mStatsLock) { + performPollLocked(true, false); + } + } + /** * Receiver that watches for {@link IConnectivityManager} to claim network * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()} @@ -506,8 +495,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { try { networkSnapshot = mNetworkManager.getNetworkStatsSummary(); uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null; + } catch (IllegalStateException e) { + Slog.w(TAG, "problem reading network stats: " + e); + return; } catch (RemoteException e) { - Slog.w(TAG, "problem reading network stats"); + Slog.w(TAG, "problem reading network stats: " + e); return; } @@ -904,6 +896,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { argSet.add(arg); } + final boolean fullHistory = argSet.contains("full"); + synchronized (mStatsLock) { // TODO: remove this testing code, since it corrupts stats if (argSet.contains("generate")) { @@ -929,7 +923,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { for (NetworkIdentitySet ident : mNetworkStats.keySet()) { final NetworkStatsHistory history = mNetworkStats.get(ident); pw.print(" ident="); pw.println(ident.toString()); - history.dump(" ", pw); + history.dump(" ", pw, fullHistory); } if (argSet.contains("detail")) { @@ -949,7 +943,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStatsHistory history = uidStats.valueAt(i); pw.print(" UID="); pw.print(uid); pw.print(" tag="); pw.println(tag); - history.dump(" ", pw); + history.dump(" ", pw, fullHistory); } } } @@ -1057,13 +1051,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return Settings.Secure.getLong(mResolver, name, def); } - public boolean getEnabled() { - if (!new File("/proc/net/xt_qtaguid/ctrl").exists()) { - Slog.w(TAG, "kernel does not support bandwidth control"); - return false; - } - return Settings.Secure.getInt(mResolver, NETSTATS_ENABLED, 1) != 0; - } public long getPollInterval() { return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS); } diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index d10aa97..11ccd60 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -307,7 +307,7 @@ class Installer { } public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, - PackageStats pStats) { + String asecPath, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -315,17 +315,20 @@ class Installer { builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); + builder.append(' '); + builder.append(asecPath != null ? asecPath : "!"); String s = transaction(builder.toString()); String res[] = s.split(" "); - if ((res == null) || (res.length != 4)) { + if ((res == null) || (res.length != 5)) { return -1; } try { pStats.codeSize = Long.parseLong(res[1]); pStats.dataSize = Long.parseLong(res[2]); pStats.cacheSize = Long.parseLong(res[3]); + pStats.externalCodeSize = Long.parseLong(res[4]); return Integer.parseInt(res[0]); } catch (NumberFormatException e) { return -1; diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 5a9dae9..d6a15e6 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -75,6 +75,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; +import android.os.FileUtils.FileStatus; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -156,7 +157,6 @@ public class PackageManagerService extends IPackageManager.Stub { private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; - private static final int KEYCHAIN_UID = Process.KEYCHAIN_UID; static final int FIRST_APPLICATION_UID = Process.FIRST_APPLICATION_UID; static final int MAX_APPLICATION_UIDS = 1000; @@ -760,10 +760,6 @@ public class PackageManagerService extends IPackageManager.Stub { MULTIPLE_APPLICATION_UIDS ? NFC_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); - mSettings.addSharedUserLPw("android.uid.keychain", - MULTIPLE_APPLICATION_UIDS - ? KEYCHAIN_UID : FIRST_APPLICATION_UID, - ApplicationInfo.FLAG_SYSTEM); String separateProcesses = SystemProperties.get("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { @@ -782,16 +778,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSeparateProcesses = null; } - Installer installer = new Installer(); - // Little hacky thing to check if installd is here, to determine - // whether we are running on the simulator and thus need to take - // care of building the /data file structure ourself. - // (apparently the sim now has a working installer) - if (installer.ping() && Process.supportsProcesses()) { - mInstaller = installer; - } else { - mInstaller = null; - } + mInstaller = new Installer(); WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); Display d = wm.getDefaultDisplay(); @@ -810,17 +797,6 @@ public class PackageManagerService extends IPackageManager.Stub { mUserManager = new UserManager(mInstaller, mUserAppDataDir); - if (mInstaller == null) { - // Make sure these dirs exist, when we are running in - // the simulator. - // Make a wide-open directory for random misc stuff. - File miscDir = new File(dataDir, "misc"); - miscDir.mkdirs(); - mAppDataDir.mkdirs(); - mUserAppDataDir.mkdirs(); - mDrmAppPrivateInstallDir.mkdirs(); - } - readPermissions(); mRestoredSettings = mSettings.readLPw(); @@ -842,104 +818,102 @@ public class PackageManagerService extends IPackageManager.Stub { mFrameworkDir = new File(Environment.getRootDirectory(), "framework"); mDalvikCacheDir = new File(dataDir, "dalvik-cache"); - if (mInstaller != null) { - boolean didDexOpt = false; - - /** - * Out of paranoia, ensure that everything in the boot class - * path has been dexed. - */ - String bootClassPath = System.getProperty("java.boot.class.path"); - if (bootClassPath != null) { - String[] paths = splitString(bootClassPath, ':'); - for (int i=0; i<paths.length; i++) { - try { - if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) { - libFiles.add(paths[i]); - mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true); - didDexOpt = true; - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Boot class path not found: " + paths[i]); - } catch (IOException e) { - Slog.w(TAG, "Exception reading boot class path: " + paths[i], e); + boolean didDexOpt = false; + + /** + * Out of paranoia, ensure that everything in the boot class + * path has been dexed. + */ + String bootClassPath = System.getProperty("java.boot.class.path"); + if (bootClassPath != null) { + String[] paths = splitString(bootClassPath, ':'); + for (int i=0; i<paths.length; i++) { + try { + if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) { + libFiles.add(paths[i]); + mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true); + didDexOpt = true; } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Boot class path not found: " + paths[i]); + } catch (IOException e) { + Slog.w(TAG, "Exception reading boot class path: " + paths[i], e); } - } else { - Slog.w(TAG, "No BOOTCLASSPATH found!"); } + } else { + Slog.w(TAG, "No BOOTCLASSPATH found!"); + } - /** - * Also ensure all external libraries have had dexopt run on them. - */ - if (mSharedLibraries.size() > 0) { - Iterator<String> libs = mSharedLibraries.values().iterator(); - while (libs.hasNext()) { - String lib = libs.next(); - try { - if (dalvik.system.DexFile.isDexOptNeeded(lib)) { - libFiles.add(lib); - mInstaller.dexopt(lib, Process.SYSTEM_UID, true); - didDexOpt = true; - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Library not found: " + lib); - } catch (IOException e) { - Slog.w(TAG, "Exception reading library: " + lib, e); + /** + * Also ensure all external libraries have had dexopt run on them. + */ + if (mSharedLibraries.size() > 0) { + Iterator<String> libs = mSharedLibraries.values().iterator(); + while (libs.hasNext()) { + String lib = libs.next(); + try { + if (dalvik.system.DexFile.isDexOptNeeded(lib)) { + libFiles.add(lib); + mInstaller.dexopt(lib, Process.SYSTEM_UID, true); + didDexOpt = true; } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Library not found: " + lib); + } catch (IOException e) { + Slog.w(TAG, "Exception reading library: " + lib, e); } } + } - // Gross hack for now: we know this file doesn't contain any - // code, so don't dexopt it to avoid the resulting log spew. - libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk"); - - /** - * And there are a number of commands implemented in Java, which - * we currently need to do the dexopt on so that they can be - * run from a non-root shell. - */ - String[] frameworkFiles = mFrameworkDir.list(); - if (frameworkFiles != null) { - for (int i=0; i<frameworkFiles.length; i++) { - File libPath = new File(mFrameworkDir, frameworkFiles[i]); - String path = libPath.getPath(); - // Skip the file if we alrady did it. - if (libFiles.contains(path)) { - continue; - } - // Skip the file if it is not a type we want to dexopt. - if (!path.endsWith(".apk") && !path.endsWith(".jar")) { - continue; - } - try { - if (dalvik.system.DexFile.isDexOptNeeded(path)) { - mInstaller.dexopt(path, Process.SYSTEM_UID, true); - didDexOpt = true; - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Jar not found: " + path); - } catch (IOException e) { - Slog.w(TAG, "Exception reading jar: " + path, e); + // Gross hack for now: we know this file doesn't contain any + // code, so don't dexopt it to avoid the resulting log spew. + libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk"); + + /** + * And there are a number of commands implemented in Java, which + * we currently need to do the dexopt on so that they can be + * run from a non-root shell. + */ + String[] frameworkFiles = mFrameworkDir.list(); + if (frameworkFiles != null) { + for (int i=0; i<frameworkFiles.length; i++) { + File libPath = new File(mFrameworkDir, frameworkFiles[i]); + String path = libPath.getPath(); + // Skip the file if we alrady did it. + if (libFiles.contains(path)) { + continue; + } + // Skip the file if it is not a type we want to dexopt. + if (!path.endsWith(".apk") && !path.endsWith(".jar")) { + continue; + } + try { + if (dalvik.system.DexFile.isDexOptNeeded(path)) { + mInstaller.dexopt(path, Process.SYSTEM_UID, true); + didDexOpt = true; } + } catch (FileNotFoundException e) { + Slog.w(TAG, "Jar not found: " + path); + } catch (IOException e) { + Slog.w(TAG, "Exception reading jar: " + path, e); } } + } - if (didDexOpt) { - // If we had to do a dexopt of one of the previous - // things, then something on the system has changed. - // Consider this significant, and wipe away all other - // existing dexopt files to ensure we don't leave any - // dangling around. - String[] files = mDalvikCacheDir.list(); - if (files != null) { - for (int i=0; i<files.length; i++) { - String fn = files[i]; - if (fn.startsWith("data@app@") - || fn.startsWith("data@app-private@")) { - Slog.i(TAG, "Pruning dalvik file: " + fn); - (new File(mDalvikCacheDir, fn)).delete(); - } + if (didDexOpt) { + // If we had to do a dexopt of one of the previous + // things, then something on the system has changed. + // Consider this significant, and wipe away all other + // existing dexopt files to ensure we don't leave any + // dangling around. + String[] files = mDalvikCacheDir.list(); + if (files != null) { + for (int i=0; i<files.length; i++) { + String fn = files[i]; + if (fn.startsWith("data@app@") + || fn.startsWith("data@app-private@")) { + Slog.i(TAG, "Pruning dalvik file: " + fn); + (new File(mDalvikCacheDir, fn)).delete(); } } } @@ -969,11 +943,9 @@ public class PackageManagerService extends IPackageManager.Stub { scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); - if (mInstaller != null) { - if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); - mInstaller.moveFiles(); - } - + if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); + mInstaller.moveFiles(); + // Prune any system packages that no longer exist. Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { @@ -985,19 +957,12 @@ public class PackageManagerService extends IPackageManager.Stub { String msg = "System package " + ps.name + " no longer exists; wiping its data"; reportSettingsProblem(Log.WARN, msg); - if (mInstaller != null) { - mInstaller.remove(ps.name, 0); - mUserManager.removePackageForAllUsers(ps.name); - } + mInstaller.remove(ps.name, 0); + mUserManager.removePackageForAllUsers(ps.name); } } mAppInstallDir = new File(dataDir, "app"); - if (mInstaller == null) { - // Make sure these dirs exist, when we are running in - // the simulator. - mAppInstallDir.mkdirs(); // scanDirLI() assumes this dir exists - } //look for any incomplete package installations ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr(); //clean up list @@ -1071,19 +1036,12 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); - if (mInstaller != null) { - int retCode = mInstaller.remove(ps.name, 0); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove app data directory for package: " - + ps.name + ", retcode=" + retCode); - } else { - mUserManager.removePackageForAllUsers(ps.name); - } + int retCode = mInstaller.remove(ps.name, 0); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove app data directory for package: " + + ps.name + ", retcode=" + retCode); } else { - //for emulator - PackageParser.Package pkg = mPackages.get(ps.name); - File dataDir = new File(pkg.applicationInfo.dataDir); - dataDir.delete(); + mUserManager.removePackageForAllUsers(ps.name); } if (ps.codePath != null) { if (!ps.codePath.delete()) { @@ -1566,12 +1524,10 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - if (mInstaller != null) { - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); - } - } //end if mInstaller + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); + } if (observer != null) { try { observer.onRemoveCompleted(null, (retCode >= 0)); @@ -1591,11 +1547,9 @@ public class PackageManagerService extends IPackageManager.Stub { public void run() { mHandler.removeCallbacks(this); int retCode = -1; - if (mInstaller != null) { - retCode = mInstaller.freeCache(freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); - } + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Slog.w(TAG, "Couldn't clear application caches"); } if(pi != null) { try { @@ -2854,7 +2808,7 @@ public class PackageManagerService extends IPackageManager.Stub { private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) { boolean performed = false; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0 && mInstaller != null) { + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { String path = pkg.mScanPath; int ret = 0; try { @@ -3239,42 +3193,39 @@ public class PackageManagerService extends IPackageManager.Stub { mOutPermissions[1] = 0; FileUtils.getPermissions(dataPath.getPath(), mOutPermissions); - // If we have mismatched owners for the data path, we have a - // problem (unless we're running in the simulator.) - if (mOutPermissions[1] != pkg.applicationInfo.uid && Process.supportsProcesses()) { + // If we have mismatched owners for the data path, we have a problem. + if (mOutPermissions[1] != pkg.applicationInfo.uid) { boolean recovered = false; if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { // If this is a system app, we can at least delete its // current data so the application will still work. - if (mInstaller != null) { - int ret = mInstaller.remove(pkgName, 0); - if (ret >= 0) { - // TODO: Kill the processes first - // Remove the data directories for all users - mUserManager.removePackageForAllUsers(pkgName); - // Old data gone! - String msg = "System package " + pkg.packageName - + " has changed from uid: " - + mOutPermissions[1] + " to " - + pkg.applicationInfo.uid + "; old data erased"; + int ret = mInstaller.remove(pkgName, 0); + if (ret >= 0) { + // TODO: Kill the processes first + // Remove the data directories for all users + mUserManager.removePackageForAllUsers(pkgName); + // Old data gone! + String msg = "System package " + pkg.packageName + + " has changed from uid: " + + mOutPermissions[1] + " to " + + pkg.applicationInfo.uid + "; old data erased"; + reportSettingsProblem(Log.WARN, msg); + recovered = true; + + // And now re-install the app. + ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, + pkg.applicationInfo.uid); + if (ret == -1) { + // Ack should not happen! + msg = "System package " + pkg.packageName + + " could not have data directory re-created after delete."; reportSettingsProblem(Log.WARN, msg); - recovered = true; - - // And now re-install the app. - ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); - if (ret == -1) { - // Ack should not happen! - msg = "System package " + pkg.packageName - + " could not have data directory re-created after delete."; - reportSettingsProblem(Log.WARN, msg); - mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - return null; - } - // Create data directories for all users - mUserManager.installPackageForAllUsers(pkgName, - pkg.applicationInfo.uid); + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; } + // Create data directories for all users + mUserManager.installPackageForAllUsers(pkgName, + pkg.applicationInfo.uid); } if (!recovered) { mHasSystemUidErrors = true; @@ -3307,25 +3258,16 @@ public class PackageManagerService extends IPackageManager.Stub { Log.v(TAG, "Want this data dir: " + dataPath); } //invoke installer to do the actual installation - if (mInstaller != null) { - int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.uid); - if (ret < 0) { - // Error from installer - mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - return null; - } - // Create data directories for all users - mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); - } else { - dataPath.mkdirs(); - if (dataPath.exists()) { - FileUtils.setPermissions( - dataPath.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, - pkg.applicationInfo.uid, pkg.applicationInfo.uid); - } + int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, + pkg.applicationInfo.uid); + if (ret < 0) { + // Error from installer + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; } + // Create data directories for all users + mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); + if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { @@ -3356,65 +3298,62 @@ public class PackageManagerService extends IPackageManager.Stub { pkgSetting.uidError = uidError; } - // If we're running in the simulator, we don't need to unpack anything. - if (mInstaller != null) { - String path = scanFile.getPath(); - /* Note: We don't want to unpack the native binaries for - * system applications, unless they have been updated - * (the binaries are already under /system/lib). - * Also, don't unpack libs for apps on the external card - * since they should have their libraries in the ASEC - * container already. - * - * In other words, we're going to unpack the binaries - * only for non-system apps and system app upgrades. - */ - if (pkg.applicationInfo.nativeLibraryDir != null) { - try { - final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); - final String dataPathString = dataPath.getCanonicalFile().getPath(); + String path = scanFile.getPath(); + /* Note: We don't want to unpack the native binaries for + * system applications, unless they have been updated + * (the binaries are already under /system/lib). + * Also, don't unpack libs for apps on the external card + * since they should have their libraries in the ASEC + * container already. + * + * In other words, we're going to unpack the binaries + * only for non-system apps and system app upgrades. + */ + if (pkg.applicationInfo.nativeLibraryDir != null) { + try { + final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); + final String dataPathString = dataPath.getCanonicalFile().getPath(); - if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { - /* - * Upgrading from a previous version of the OS sometimes - * leaves native libraries in the /data/data/<app>/lib - * directory for system apps even when they shouldn't be. - * Recent changes in the JNI library search path - * necessitates we remove those to match previous behavior. - */ - if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) { - Log.i(TAG, "removed obsolete native libraries for system package " - + path); - } - } else if (nativeLibraryDir.getCanonicalFile().getParent() - .equals(dataPathString)) { - /* - * If this is an internal application or our - * nativeLibraryPath points to our data directory, unpack - * the libraries. The native library path pointing to the - * data directory for an application in an ASEC container - * can happen for older apps that existed before an OTA to - * Gingerbread. - */ - Slog.i(TAG, "Unpacking native libraries for " + path); - mInstaller.unlinkNativeLibraryDirectory(dataPathString); - NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir); - } else { - Slog.i(TAG, "Linking native library dir for " + path); - mInstaller.linkNativeLibraryDirectory(dataPathString, - pkg.applicationInfo.nativeLibraryDir); + if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { + /* + * Upgrading from a previous version of the OS sometimes + * leaves native libraries in the /data/data/<app>/lib + * directory for system apps even when they shouldn't be. + * Recent changes in the JNI library search path + * necessitates we remove those to match previous behavior. + */ + if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) { + Log.i(TAG, "removed obsolete native libraries for system package " + + path); } - } catch (IOException ioe) { - Log.e(TAG, "Unable to get canonical file " + ioe.toString()); + } else if (nativeLibraryDir.getCanonicalFile().getParent() + .equals(dataPathString)) { + /* + * If this is an internal application or our + * nativeLibraryPath points to our data directory, unpack + * the libraries. The native library path pointing to the + * data directory for an application in an ASEC container + * can happen for older apps that existed before an OTA to + * Gingerbread. + */ + Slog.i(TAG, "Unpacking native libraries for " + path); + mInstaller.unlinkNativeLibraryDirectory(dataPathString); + NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir); + } else { + Slog.i(TAG, "Linking native library dir for " + path); + mInstaller.linkNativeLibraryDirectory(dataPathString, + pkg.applicationInfo.nativeLibraryDir); } + } catch (IOException ioe) { + Log.e(TAG, "Unable to get canonical file " + ioe.toString()); } - pkg.mScanPath = path; + } + pkg.mScanPath = path; - if ((scanMode&SCAN_NO_DEX) == 0) { - if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { - mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; - return null; - } + if ((scanMode&SCAN_NO_DEX) == 0) { + if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { + mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; + return null; } } @@ -4887,8 +4826,7 @@ public class PackageManagerService extends IPackageManager.Stub { private final IPackageStatsObserver mObserver; - public MeasureParams(PackageStats stats, boolean success, - IPackageStatsObserver observer) { + public MeasureParams(PackageStats stats, boolean success, IPackageStatsObserver observer) { mObserver = observer; mStats = stats; mSuccess = success; @@ -5439,7 +5377,7 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceDir = getCodePath(); - if (cleanUp() && mInstaller != null) { + if (cleanUp()) { int retCode = mInstaller.rmdex(sourceDir); if (retCode < 0) { Slog.w(TAG, "Couldn't remove dex file for package: " @@ -5480,6 +5418,17 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * Extract the MountService "container ID" from the full code path of an + * .apk. + */ + static String cidFromCodePath(String fullCodePath) { + int eidx = fullCodePath.lastIndexOf("/"); + String subStr1 = fullCodePath.substring(0, eidx); + int sidx = subStr1.lastIndexOf("/"); + return subStr1.substring(sidx+1, eidx); + } + class SdInstallArgs extends InstallArgs { static final String RES_FILE_NAME = "pkg.apk"; @@ -5656,14 +5605,12 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanUpResourcesLI() { String sourceFile = getCodePath(); // Remove dex file - if (mInstaller != null) { - int retCode = mInstaller.rmdex(sourceFile); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove dex file for package: " - + " at location " - + sourceFile.toString() + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion - } + int retCode = mInstaller.rmdex(sourceFile); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove dex file for package: " + + " at location " + + sourceFile.toString() + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion } cleanUp(); } @@ -6071,9 +6018,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if((res.returnCode = setPermissionsLI(newPackage)) != PackageManager.INSTALL_SUCCEEDED) { - if (mInstaller != null) { - mInstaller.rmdex(newPackage.mScanPath); - } + mInstaller.rmdex(newPackage.mScanPath); return; } else { Log.d(TAG, "New package installed in " + newPackage.mPath); @@ -6201,15 +6146,8 @@ public class PackageManagerService extends IPackageManager.Stub { } finally { //TODO clean up the extracted public files } - if (mInstaller != null) { - retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath), - newPackage.applicationInfo.uid); - } else { - final int filePermissions = - FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP; - retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1, - newPackage.applicationInfo.uid); - } + retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath), + newPackage.applicationInfo.uid); } else { // The permissions on the resource file was set when it was copied for // non forward locked apps and apps on sdcard @@ -6472,25 +6410,14 @@ public class PackageManagerService extends IPackageManager.Stub { deletedPs = mSettings.mPackages.get(packageName); } if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { - if (mInstaller != null) { - int retCode = mInstaller.remove(packageName, 0); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove app data or cache directory for package: " - + packageName + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion - } else { - // TODO: Kill the processes first - mUserManager.removePackageForAllUsers(packageName); - } + int retCode = mInstaller.remove(packageName, 0); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove app data or cache directory for package: " + + packageName + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion } else { - // for simulator - File dataDir; - // reader - synchronized (mPackages) { - PackageParser.Package pkg = mPackages.get(packageName); - dataDir = new File(pkg.applicationInfo.dataDir); - } - dataDir.delete(); + // TODO: Kill the processes first + mUserManager.removePackageForAllUsers(packageName); } schedulePackageCleaning(packageName); } @@ -6739,13 +6666,11 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } } - if (mInstaller != null) { - int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove cache files for package: " - + packageName); - return false; - } + int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove cache files for package: " + + packageName); + return false; } return true; } @@ -6791,13 +6716,11 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - if (mInstaller != null) { - int retCode = mInstaller.deleteCacheFiles(packageName); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove cache files for package: " - + packageName); - return false; - } + int retCode = mInstaller.deleteCacheFiles(packageName); + if (retCode < 0) { + Slog.w(TAG, "Couldn't remove cache files for package: " + + packageName); + return false; } return true; } @@ -6831,6 +6754,7 @@ public class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package p; boolean dataOnly = false; + String asecPath = null; synchronized (mPackages) { p = mPackages.get(packageName); if(p == null) { @@ -6842,6 +6766,12 @@ public class PackageManagerService extends IPackageManager.Stub { } p = ps.pkg; } + if (p != null && isExternal(p)) { + String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir); + if (secureContainerId != null) { + asecPath = PackageHelper.getSdFilesystem(secureContainerId); + } + } } String publicSrcDir = null; if(!dataOnly) { @@ -6850,16 +6780,15 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; - } - if (mInstaller != null) { - int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, pStats); - if (res < 0) { - return false; - } else { - return true; + if (isForwardLocked(p)) { + publicSrcDir = applicationInfo.publicSourceDir; } } + int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, + asecPath, pStats); + if (res < 0) { + return false; + } return true; } diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index 918f1b6..1ab570a 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -45,7 +45,6 @@ import android.os.storage.StorageVolume; import android.os.SystemProperties; import android.os.UEventObserver; import android.provider.Settings; -import android.util.Log; import android.util.Slog; import java.io.File; @@ -62,7 +61,7 @@ import java.util.List; public class UsbDeviceManager { private static final String TAG = UsbDeviceManager.class.getSimpleName(); - private static final boolean LOG = false; + private static final boolean DEBUG = false; private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; @@ -73,7 +72,9 @@ public class UsbDeviceManager { private static final String STATE_PATH = "/sys/class/android_usb/android0/state"; private static final String MASS_STORAGE_FILE_PATH = - "/sys/class/android_usb/f_mass_storage/lun/file"; + "/sys/class/android_usb/android0/f_mass_storage/lun/file"; + private static final String RNDIS_ETH_ADDR_PATH = + "/sys/class/android_usb/android0/f_rndis/ethaddr"; private static final int MSG_UPDATE_STATE = 0; private static final int MSG_ENABLE_ADB = 1; @@ -93,18 +94,9 @@ public class UsbDeviceManager { private final UsbSettingsManager mSettingsManager; private NotificationManager mNotificationManager; private final boolean mHasUsbAccessory; - - // for USB connected notification - private boolean mUsbNotificationShown; private boolean mUseUsbNotification; - private Notification mUsbNotification; - - // for adb connected notification - private boolean mAdbNotificationShown; - private Notification mAdbNotification; private boolean mAdbEnabled; - private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver() { super(null); @@ -117,115 +109,20 @@ public class UsbDeviceManager { } } - private void updateUsbNotification(boolean connected) { - if (mNotificationManager == null || !mUseUsbNotification) return; - if (connected) { - if (!mUsbNotificationShown) { - Resources r = mContext.getResources(); - CharSequence title = r.getText( - com.android.internal.R.string.usb_preferences_notification_title); - CharSequence message = r.getText( - com.android.internal.R.string.usb_preferece_notification_message); - - if (mUsbNotification == null) { - mUsbNotification = new Notification(); - mUsbNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb; - mUsbNotification.when = 0; - mUsbNotification.flags = Notification.FLAG_ONGOING_EVENT; - mUsbNotification.tickerText = title; - mUsbNotification.defaults = 0; // please be quiet - mUsbNotification.sound = null; - mUsbNotification.vibrate = null; - } - - Intent intent = new Intent(); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - intent.setClassName("com.android.systemui", - "com.android.systemui.usb.UsbPreferenceActivity"); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, - intent, 0); - - mUsbNotification.setLatestEventInfo(mContext, title, message, pi); - - mUsbNotificationShown = true; - mNotificationManager.notify( - com.android.internal.R.string.usb_preferences_notification_title, - mUsbNotification); - } - - } else if (mUsbNotificationShown) { - mUsbNotificationShown = false; - mNotificationManager.cancel( - com.android.internal.R.string.usb_preferences_notification_title); - } - } - - private void updateAdbNotification(boolean adbEnabled) { - if (mNotificationManager == null) return; - if (adbEnabled) { - if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; - - if (!mAdbNotificationShown) { - Resources r = mContext.getResources(); - CharSequence title = r.getText( - com.android.internal.R.string.adb_active_notification_title); - CharSequence message = r.getText( - com.android.internal.R.string.adb_active_notification_message); - - if (mAdbNotification == null) { - mAdbNotification = new Notification(); - mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb; - mAdbNotification.when = 0; - mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; - mAdbNotification.tickerText = title; - mAdbNotification.defaults = 0; // please be quiet - mAdbNotification.sound = null; - mAdbNotification.vibrate = null; - } - - Intent intent = new Intent( - Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - // Note: we are hard-coding the component because this is - // an important security UI that we don't want anyone - // intercepting. - intent.setComponent(new ComponentName("com.android.settings", - "com.android.settings.DevelopmentSettings")); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, - intent, 0); - - mAdbNotification.setLatestEventInfo(mContext, title, message, pi); - - mAdbNotificationShown = true; - mNotificationManager.notify( - com.android.internal.R.string.adb_active_notification_title, - mAdbNotification); - } - } else if (mAdbNotificationShown) { - mAdbNotificationShown = false; - mNotificationManager.cancel( - com.android.internal.R.string.adb_active_notification_title); - } - } - /* * Listens for uevent messages from the kernel to monitor the USB state */ private final UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.v(TAG, "USB UEVENT: " + event.toString()); - } + if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); if (state != null) { mHandler.updateState(state); } else if ("START".equals(accessory)) { - Slog.d(TAG, "got accessory start"); + if (DEBUG) Slog.d(TAG, "got accessory start"); setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); } } @@ -237,6 +134,7 @@ public class UsbDeviceManager { mSettingsManager = settingsManager; PackageManager pm = mContext.getPackageManager(); mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); + initRndisAddress(); // create a thread for our Handler HandlerThread thread = new HandlerThread("UsbDeviceManager", @@ -271,6 +169,29 @@ public class UsbDeviceManager { mHandler.sendEmptyMessage(MSG_SYSTEM_READY); } + private static void initRndisAddress() { + // configure RNDIS ethernet address based on our serial number using the same algorithm + // we had been previously using in kernel board files + final int ETH_ALEN = 6; + int address[] = new int[ETH_ALEN]; + // first byte is 0x02 to signify a locally administered address + address[0] = 0x02; + + String serial = SystemProperties.get("ro.serialno", "1234567890ABCDEF"); + int serialLength = serial.length(); + // XOR the USB serial across the remaining 5 bytes + for (int i = 0; i < serialLength; i++) { + address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i); + } + String addrString = String.format("%02X:%02X:%02X:%02X:%02X:%02X", + address[0], address[1], address[2], address[3], address[4], address[5]); + try { + FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString); + } catch (IOException e) { + Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH); + } + } + private static String addFunction(String functions, String function) { if (!containsFunction(functions, function)) { if (functions.length() > 0) { @@ -319,16 +240,32 @@ public class UsbDeviceManager { private String mDefaultFunctions; private UsbAccessory mCurrentAccessory; private boolean mDeferAccessoryAttached; + private int mUsbNotificationId; + private boolean mAdbNotificationShown; + + private static final int NOTIFICATION_NONE = 0; + private static final int NOTIFICATION_MTP = 1; + private static final int NOTIFICATION_PTP = 2; + private static final int NOTIFICATION_INSTALLER = 3; + private static final int NOTIFICATION_ADB = 4; public UsbHandler() { - // Read initial USB state try { + // sanity check the sys.usb.config system property + // this may be necessary if we crashed while switching USB configurations + String config = SystemProperties.get("sys.usb.config", "none"); + if (config.equals("none")) { + String persistConfig = SystemProperties.get("persist.sys.usb.config", "none"); + Slog.w(TAG, "resetting config to persistent property: " + persistConfig); + SystemProperties.set("sys.usb.config", persistConfig); + } + + // Read initial USB state mCurrentFunctions = FileUtils.readTextFile( new File(FUNCTIONS_PATH), 0, null).trim(); mDefaultFunctions = mCurrentFunctions; String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); - mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB); // Upgrade step for previous versions that used persist.service.adb.enable @@ -414,12 +351,12 @@ public class UsbDeviceManager { } catch (InterruptedException e) { } } - Log.e(TAG, "waitForState(" + state + ") FAILED"); + Slog.e(TAG, "waitForState(" + state + ") FAILED"); return false; } private boolean setUsbConfig(String config) { - Log.d(TAG, "setUsbConfig(" + config + ")"); + if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); // set the new configuration SystemProperties.set("sys.usb.config", config); return waitForState(config); @@ -428,7 +365,7 @@ public class UsbDeviceManager { private void doSetCurrentFunctions(String functions) { if (!mCurrentFunctions.equals(functions)) { if (!setUsbConfig("none") || !setUsbConfig(functions)) { - Log.e(TAG, "Failed to switch USB configuration to " + functions); + Slog.e(TAG, "Failed to switch USB configuration to " + functions); // revert to previous configuration if we fail setUsbConfig(mCurrentFunctions); } else { @@ -438,6 +375,7 @@ public class UsbDeviceManager { } private void setAdbEnabled(boolean enable) { + if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; String functions; @@ -449,7 +387,7 @@ public class UsbDeviceManager { functions = removeFunction(mDefaultFunctions, UsbManager.USB_FUNCTION_ADB); } setCurrentFunction(functions, true); - updateAdbNotification(mAdbEnabled && mConnected); + updateAdbNotification(); } } @@ -469,7 +407,7 @@ public class UsbDeviceManager { String[] strings = nativeGetAccessoryStrings(); if (strings != null) { mCurrentAccessory = new UsbAccessory(strings); - Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); + Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); // defer accessoryAttached if system is not ready if (mSystemReady) { mSettingsManager.accessoryAttached(mCurrentAccessory); @@ -477,12 +415,12 @@ public class UsbDeviceManager { mDeferAccessoryAttached = true; } } else { - Log.e(TAG, "nativeGetAccessoryStrings failed"); + Slog.e(TAG, "nativeGetAccessoryStrings failed"); } } else if (!mConnected) { // make sure accessory mode is off // and restore default functions - Log.d(TAG, "exited USB accessory mode"); + Slog.d(TAG, "exited USB accessory mode"); setEnabledFunctions(mDefaultFunctions); if (mCurrentAccessory != null) { @@ -517,8 +455,8 @@ public class UsbDeviceManager { case MSG_UPDATE_STATE: mConnected = (msg.arg1 == 1); mConfigured = (msg.arg2 == 1); - updateUsbNotification(mConnected); - updateAdbNotification(mAdbEnabled && mConnected); + updateUsbNotification(); + updateAdbNotification(); if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); @@ -562,8 +500,8 @@ public class UsbDeviceManager { } break; case MSG_SYSTEM_READY: - updateUsbNotification(mConnected); - updateAdbNotification(mAdbEnabled && mConnected); + updateUsbNotification(); + updateAdbNotification(); updateUsbState(); if (mCurrentAccessory != null && mDeferAccessoryAttached) { mSettingsManager.accessoryAttached(mCurrentAccessory); @@ -576,6 +514,106 @@ public class UsbDeviceManager { return mCurrentAccessory; } + private void updateUsbNotification() { + if (mNotificationManager == null || !mUseUsbNotification) return; + if (mConnected) { + Resources r = mContext.getResources(); + CharSequence title = null; + int id = NOTIFICATION_NONE; + if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { + title = r.getText( + com.android.internal.R.string.usb_mtp_notification_title); + id = NOTIFICATION_MTP; + } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) { + title = r.getText( + com.android.internal.R.string.usb_ptp_notification_title); + id = NOTIFICATION_PTP; + } else if (containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MASS_STORAGE)) { + title = r.getText( + com.android.internal.R.string.usb_cd_installer_notification_title); + id = NOTIFICATION_INSTALLER; + } else { + Slog.e(TAG, "No known USB function in updateUsbNotification"); + } + if (id != mUsbNotificationId) { + // clear notification if title needs changing + if (mUsbNotificationId != NOTIFICATION_NONE) { + mNotificationManager.cancel(mUsbNotificationId); + mUsbNotificationId = NOTIFICATION_NONE; + } + } + if (mUsbNotificationId == NOTIFICATION_NONE) { + CharSequence message = r.getText( + com.android.internal.R.string.usb_notification_message); + + Notification notification = new Notification(); + notification.icon = com.android.internal.R.drawable.stat_sys_data_usb; + notification.when = 0; + notification.flags = Notification.FLAG_ONGOING_EVENT; + notification.tickerText = title; + notification.defaults = 0; // please be quiet + notification.sound = null; + notification.vibrate = null; + + Intent intent = new Intent( + Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.setComponent(new ComponentName("com.android.settings", + "com.android.settings.UsbSettings")); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + intent, 0); + notification.setLatestEventInfo(mContext, title, message, pi); + mNotificationManager.notify(id, notification); + mUsbNotificationId = id; + } + + } else if (mUsbNotificationId != NOTIFICATION_NONE) { + mNotificationManager.cancel(mUsbNotificationId); + mUsbNotificationId = NOTIFICATION_NONE; + } + } + + private void updateAdbNotification() { + if (mNotificationManager == null) return; + if (mAdbEnabled && mConnected) { + if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; + + if (!mAdbNotificationShown) { + Resources r = mContext.getResources(); + CharSequence title = r.getText( + com.android.internal.R.string.adb_active_notification_title); + CharSequence message = r.getText( + com.android.internal.R.string.adb_active_notification_message); + + Notification notification = new Notification(); + notification.icon = com.android.internal.R.drawable.stat_sys_adb; + notification.when = 0; + notification.flags = Notification.FLAG_ONGOING_EVENT; + notification.tickerText = title; + notification.defaults = 0; // please be quiet + notification.sound = null; + notification.vibrate = null; + + Intent intent = new Intent( + Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.setComponent(new ComponentName("com.android.settings", + "com.android.settings.DevelopmentSettings")); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + intent, 0); + notification.setLatestEventInfo(mContext, title, message, pi); + mAdbNotificationShown = true; + mNotificationManager.notify(NOTIFICATION_ADB, notification); + } + } else if (mAdbNotificationShown) { + mAdbNotificationShown = false; + mNotificationManager.cancel(NOTIFICATION_ADB); + } + } + public void dump(FileDescriptor fd, PrintWriter pw) { pw.println(" USB Device State:"); pw.println(" Current Functions: " + mCurrentFunctions); @@ -592,22 +630,23 @@ public class UsbDeviceManager { } /* opens the currently attached USB accessory */ - public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { - UsbAccessory currentAccessory = mHandler.getCurrentAccessory(); - if (currentAccessory == null) { - throw new IllegalArgumentException("no accessory attached"); - } - if (!currentAccessory.equals(accessory)) { - String error = accessory.toString() - + " does not match current accessory " - + currentAccessory; - throw new IllegalArgumentException(error); - } - mSettingsManager.checkPermission(accessory); - return nativeOpenAccessory(); + public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { + UsbAccessory currentAccessory = mHandler.getCurrentAccessory(); + if (currentAccessory == null) { + throw new IllegalArgumentException("no accessory attached"); + } + if (!currentAccessory.equals(accessory)) { + String error = accessory.toString() + + " does not match current accessory " + + currentAccessory; + throw new IllegalArgumentException(error); } + mSettingsManager.checkPermission(accessory); + return nativeOpenAccessory(); + } public void setCurrentFunction(String function, boolean makeDefault) { + if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault); mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, makeDefault); } diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java index 923b049..0a0ff59 100644 --- a/services/java/com/android/server/usb/UsbHostManager.java +++ b/services/java/com/android/server/usb/UsbHostManager.java @@ -37,7 +37,6 @@ import android.os.Parcelable; import android.os.ParcelFileDescriptor; import android.os.UEventObserver; import android.provider.Settings; -import android.util.Log; import android.util.Slog; import java.io.File; @@ -112,7 +111,7 @@ public class UsbHostManager { synchronized (mLock) { if (mDevices.get(deviceName) != null) { - Log.w(TAG, "device already on mDevices list: " + deviceName); + Slog.w(TAG, "device already on mDevices list: " + deviceName); return; } @@ -148,7 +147,7 @@ public class UsbHostManager { } catch (Exception e) { // beware of index out of bound exceptions, which might happen if // a device does not set bNumEndpoints correctly - Log.e(TAG, "error parsing USB descriptors", e); + Slog.e(TAG, "error parsing USB descriptors", e); return; } diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java index 9113677..0baafbb 100644 --- a/services/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/java/com/android/server/usb/UsbSettingsManager.java @@ -35,7 +35,7 @@ import android.hardware.usb.UsbManager; import android.os.Binder; import android.os.FileUtils; import android.os.Process; -import android.util.Log; +import android.util.Slog; import android.util.SparseBooleanArray; import android.util.Xml; @@ -62,6 +62,7 @@ import java.util.List; class UsbSettingsManager { private static final String TAG = "UsbSettingsManager"; + private static final boolean DEBUG = false; private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml"); private final Context mContext; @@ -410,9 +411,9 @@ class UsbSettingsManager { } } } catch (FileNotFoundException e) { - Log.w(TAG, "settings file not found"); + if (DEBUG) Slog.d(TAG, "settings file not found"); } catch (Exception e) { - Log.e(TAG, "error reading settings file, deleting to start fresh", e); + Slog.e(TAG, "error reading settings file, deleting to start fresh", e); sSettingsFile.delete(); } finally { if (stream != null) { @@ -428,7 +429,7 @@ class UsbSettingsManager { FileOutputStream fos = null; try { FileOutputStream fstr = new FileOutputStream(sSettingsFile); - Log.d(TAG, "writing settings to " + fstr); + if (DEBUG) Slog.d(TAG, "writing settings to " + fstr); BufferedOutputStream str = new BufferedOutputStream(fstr); FastXmlSerializer serializer = new FastXmlSerializer(); serializer.setOutput(str, "utf-8"); @@ -457,7 +458,7 @@ class UsbSettingsManager { FileUtils.sync(fstr); str.close(); } catch (Exception e) { - Log.e(TAG, "error writing settings file, deleting to start fresh", e); + Slog.e(TAG, "error writing settings file, deleting to start fresh", e); sSettingsFile.delete(); } } @@ -472,7 +473,7 @@ class UsbSettingsManager { try { parser = ai.loadXmlMetaData(mPackageManager, metaDataName); if (parser == null) { - Log.w(TAG, "no meta-data for " + info); + Slog.w(TAG, "no meta-data for " + info); return false; } @@ -494,7 +495,7 @@ class UsbSettingsManager { XmlUtils.nextElement(parser); } } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + info.toString(), e); + Slog.w(TAG, "Unable to load component info " + info.toString(), e); } finally { if (parser != null) parser.close(); } @@ -553,7 +554,7 @@ class UsbSettingsManager { Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); intent.putExtra(UsbManager.EXTRA_DEVICE, device); - Log.d(TAG, "usbDeviceRemoved, sending " + intent); + if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent); mContext.sendBroadcast(intent); } @@ -604,7 +605,7 @@ class UsbSettingsManager { try { mContext.startActivity(dialogIntent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start UsbAccessoryUriActivity"); + Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); } } } @@ -652,7 +653,7 @@ class UsbSettingsManager { defaultRI.activityInfo.name)); mContext.startActivity(intent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "startActivity failed", e); + Slog.e(TAG, "startActivity failed", e); } } else { Intent resolverIntent = new Intent(); @@ -679,7 +680,7 @@ class UsbSettingsManager { try { mContext.startActivity(resolverIntent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start activity " + resolverIntent); + Slog.e(TAG, "unable to start activity " + resolverIntent); } } } @@ -733,7 +734,7 @@ class UsbSettingsManager { XmlUtils.nextElement(parser); } } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + aInfo.toString(), e); + Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e); } finally { if (parser != null) parser.close(); } @@ -751,7 +752,7 @@ class UsbSettingsManager { info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); } catch (NameNotFoundException e) { - Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); + Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e); return; } @@ -831,7 +832,7 @@ class UsbSettingsManager { try { mContext.startActivity(intent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start UsbPermissionActivity"); + Slog.e(TAG, "unable to start UsbPermissionActivity"); } finally { Binder.restoreCallingIdentity(identity); } @@ -847,7 +848,7 @@ class UsbSettingsManager { try { pi.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "requestPermission PendingIntent was cancelled"); + if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); } return; } @@ -867,7 +868,7 @@ class UsbSettingsManager { try { pi.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "requestPermission PendingIntent was cancelled"); + if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); } return; } diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index ee69311..65007f9 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -551,18 +551,6 @@ public class InputManager { android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED; } - - @SuppressWarnings("unused") - public boolean filterTouchEvents() { - return mContext.getResources().getBoolean( - com.android.internal.R.bool.config_filterTouchEvents); - } - - @SuppressWarnings("unused") - public boolean filterJumpyTouchEvents() { - return mContext.getResources().getBoolean( - com.android.internal.R.bool.config_filterJumpyTouchEvents); - } @SuppressWarnings("unused") public int getVirtualKeyQuietTimeMillis() { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 3bf309b..be21ac0 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -231,7 +231,7 @@ public class WindowManagerService extends IWindowManager.Stub // Maximum number of milliseconds to wait for input devices to be enumerated before // proceding with safe mode detection. private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; - + // Default input dispatching timeout in nanoseconds. static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; @@ -4886,8 +4886,13 @@ public class WindowManagerService extends IWindowManager.Stub int fw = frame.width(); int fh = frame.height(); - // First try reducing to fit in x dimension. - scale = width/(float)fw; + // Constrain thumbnail to smaller of screen width or height. Assumes aspect + // of thumbnail is the same as the screen (in landscape) or square. + if (dw <= dh) { + scale = width / (float) fw; // portrait + } else { + scale = height / (float) fh; // landscape + } // The screen shot will contain the entire screen. dw = (int)(dw*scale); |