diff options
Diffstat (limited to 'services/java/com')
24 files changed, 912 insertions, 283 deletions
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java index 33e712a..ea7b696 100755..100644 --- a/services/java/com/android/server/BluetoothManagerService.java +++ b/services/java/com/android/server/BluetoothManagerService.java @@ -19,6 +19,7 @@ package com.android.server; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; @@ -87,6 +88,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // and Airplane mode will have higher priority. private static final int BLUETOOTH_ON_AIRPLANE=2; + private static final int SERVICE_IBLUETOOTH = 1; + private static final int SERVICE_IBLUETOOTHGATT = 2; + private final Context mContext; // Locks are not provided for mName and mAddress. @@ -97,6 +101,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks; private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks; private IBluetooth mBluetooth; + private IBluetoothGatt mBluetoothGatt; private boolean mBinding; private boolean mUnbinding; // used inside handler thread @@ -463,6 +468,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + public IBluetoothGatt getBluetoothGatt() { + // sync protection + return mBluetoothGatt; + } + private void sendBluetoothStateCallback(boolean isUp) { int n = mStateChangeCallbacks.beginBroadcast(); if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); @@ -575,16 +585,35 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService"); + if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName()); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); + // TBD if (className.getClassName().equals(IBluetooth.class.getName())) { + if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + msg.arg1 = SERVICE_IBLUETOOTH; + // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) { + } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + msg.arg1 = SERVICE_IBLUETOOTHGATT; + } else { + Log.e(TAG, "Unknown service connected: " + className.getClassName()); + return; + } msg.obj = service; mHandler.sendMessage(msg); } public void onServiceDisconnected(ComponentName className) { // Called if we unexpected disconnected. - if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService"); + if (DBG) Log.d(TAG, "BluetoothServiceConnection, disconnected: " + + className.getClassName()); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); + if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + msg.arg1 = SERVICE_IBLUETOOTH; + } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + msg.arg1 = SERVICE_IBLUETOOTHGATT; + } else { + Log.e(TAG, "Unknown service disconnected: " + className.getClassName()); + return; + } mHandler.sendMessage(msg); } } @@ -746,13 +775,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: { - if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED"); - - //Remove timeout - mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1); IBinder service = (IBinder) msg.obj; synchronized(mConnection) { + if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { + mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); + break; + } // else must be SERVICE_IBLUETOOTH + + //Remove timeout + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + mBinding = false; mBluetooth = IBluetooth.Stub.asInterface(service); @@ -816,11 +850,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { - Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED"); + Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1); synchronized(mConnection) { - // if service is unbinded already, do nothing and return - if (mBluetooth == null) return; - mBluetooth = null; + if (msg.arg1 == SERVICE_IBLUETOOTH) { + // if service is unbinded already, do nothing and return + if (mBluetooth == null) break; + mBluetooth = null; + } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { + mBluetoothGatt = null; + break; + } else { + Log.e(TAG, "Bad msg.arg1: " + msg.arg1); + break; + } } if (mEnable) { @@ -1048,10 +1090,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { boolean isUp = (newState==BluetoothAdapter.STATE_ON); sendBluetoothStateCallback(isUp); - //If Bluetooth is off, send service down event to proxy objects, and unbind - if (!isUp && canUnbindBluetoothService()) { - sendBluetoothServiceDownCallback(); - unbindAndFinish(); + if (isUp) { + // connect to GattService + Intent i = new Intent(IBluetoothGatt.class.getName()); + if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE, + UserHandle.CURRENT)) { + Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName()); + } + } else { + //If Bluetooth is off, send service down event to proxy objects, and unbind + if (!isUp && canUnbindBluetoothService()) { + sendBluetoothServiceDownCallback(); + unbindAndFinish(); + } } } @@ -1081,9 +1132,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; } else if (off) { if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; - } else { + } else { if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; - } + } } catch (RemoteException e) { Log.e(TAG, "getState()", e); break; @@ -1091,9 +1142,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (on || off) { SystemClock.sleep(300); - } else { + } else { SystemClock.sleep(50); - } + } i++; } Log.e(TAG,"waitForOnOff time out"); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 6dcb403..9e06db8 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -74,6 +74,7 @@ import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; +import android.os.Messenger; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; @@ -2284,9 +2285,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { } // Update 464xlat state. - // TODO: Move to handleConnect() NetworkStateTracker tracker = mNetTrackers[netType]; if (mClat.requiresClat(netType, tracker)) { + // If the connection was previously using clat, but is not using it now, stop the clat + // daemon. Normally, this happens automatically when the connection disconnects, but if + // the disconnect is not reported, or if the connection's LinkProperties changed for + // some other reason (e.g., handoff changes the IP addresses on the link), it would + // still be running. If it's not running, then stopping it is a no-op. + if (Nat464Xlat.isRunningClat(curLp) && !Nat464Xlat.isRunningClat(newLp)) { + mClat.stopClat(); + } + // If the link requires clat to be running, then start the daemon now. if (mNetTrackers[netType].getNetworkInfo().isConnected()) { mClat.startClat(tracker); } else { @@ -3220,7 +3229,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { throwIfLockdownEnabled(); try { int type = mActiveDefaultNetwork; - if (ConnectivityManager.isNetworkTypeValid(type)) { + if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) { mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName()); return true; } @@ -3425,4 +3434,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { throw new IllegalStateException("Unavailable in lockdown mode"); } } + + public void supplyMessenger(int networkType, Messenger messenger) { + enforceConnectivityInternalPermission(); + + if (isNetworkTypeValid(networkType) && mNetTrackers[networkType] != null) { + mNetTrackers[networkType].supplyMessenger(messenger); + } + } } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index d3e7c24..ab70e6f 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -2443,7 +2443,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { ipm.setApplicationEnabledSetting(packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, - PackageManager.DONT_KILL_APP, userId); + PackageManager.DONT_KILL_APP, userId, "DevicePolicyManager"); } } catch (RemoteException e) { } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 2d53023..3b541ec 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1598,7 +1598,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(), PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, - PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId()); + PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(), + mContext.getBasePackageName()); } } catch (RemoteException e) { } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 2210a18..d2acb40 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -839,33 +839,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub return event.getMessage().endsWith("started"); } - // TODO(BT) Remove - @Override - public void startReverseTethering(String iface) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - // cmd is "tether start first_start first_stop second_start second_stop ..." - // an odd number of addrs will fail - try { - mConnector.execute("tether", "start-reverse", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - BluetoothTetheringDataTracker.getInstance().startReverseTether(iface); - - } - - // TODO(BT) Remove - @Override - public void stopReverseTethering() { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("tether", "stop-reverse"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - BluetoothTetheringDataTracker.getInstance().stopReverseTether(); - } - @Override public void tetherInterface(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4631395..a30fc3b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -862,6 +862,11 @@ class ServerThread extends Thread { public void run() { Slog.i(TAG, "Making services ready"); + try { + ActivityManagerService.self().startObservingNativeCrashes(); + } catch (Throwable e) { + reportWtf("observing native crashes", e); + } if (!headless) startSystemUi(contextF); try { if (mountServiceF != null) mountServiceF.systemReady(); diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 1663106..167e7af 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -88,7 +88,6 @@ public class Watchdog extends Thread { AlarmManagerService mAlarm; ActivityManagerService mActivity; boolean mCompleted; - boolean mForceKillSystem; Monitor mCurrentMonitor; int mPhonePid; @@ -135,7 +134,9 @@ public class Watchdog extends Thread { final int size = mMonitors.size(); for (int i = 0 ; i < size ; i++) { - mCurrentMonitor = mMonitors.get(i); + synchronized (Watchdog.this) { + mCurrentMonitor = mMonitors.get(i); + } mCurrentMonitor.monitor(); } @@ -388,6 +389,8 @@ public class Watchdog extends Thread { mCompleted = false; mHandler.sendEmptyMessage(MONITOR); + + final String name; synchronized (this) { long timeout = TIME_TO_WAIT; @@ -396,16 +399,16 @@ public class Watchdog extends Thread { // to timeout on is asleep as well and won't have a chance to run, causing a false // positive on when to kill things. long start = SystemClock.uptimeMillis(); - while (timeout > 0 && !mForceKillSystem) { + while (timeout > 0) { try { - wait(timeout); // notifyAll() is called when mForceKillSystem is set + wait(timeout); } catch (InterruptedException e) { Log.wtf(TAG, e); } timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start); } - if (mCompleted && !mForceKillSystem) { + if (mCompleted) { // The monitors have returned. waitedHalf = false; continue; @@ -421,14 +424,14 @@ public class Watchdog extends Thread { waitedHalf = true; continue; } + + name = (mCurrentMonitor != null) ? + mCurrentMonitor.getClass().getName() : "null"; } // If we got here, that means that the system is most likely hung. // First collect stack traces from all threads of the system process. // Then kill this process so that the system will restart. - - final String name = (mCurrentMonitor != null) ? - mCurrentMonitor.getClass().getName() : "null"; EventLog.writeEvent(EventLogTags.WATCHDOG, name); ArrayList<Integer> pids = new ArrayList<Integer>(); diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java index c4b98ad..14d808f 100644 --- a/services/java/com/android/server/accounts/AccountManagerService.java +++ b/services/java/com/android/server/accounts/AccountManagerService.java @@ -58,6 +58,7 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -115,6 +116,7 @@ public class AccountManagerService // Messages that can be sent on mHandler private static final int MESSAGE_TIMED_OUT = 3; + private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4; private final IAccountAuthenticatorCache mAuthenticatorCache; @@ -270,7 +272,7 @@ public class AccountManagerService private UserManager getUserManager() { if (mUserManager == null) { - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mUserManager = UserManager.get(mContext); } return mUserManager; } @@ -541,17 +543,22 @@ public class AccountManagerService } } - public boolean addAccount(Account account, String password, Bundle extras) { + @Override + public boolean addAccountExplicitly(Account account, String password, Bundle extras) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "addAccount: " + account + Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + Binder.getCallingUid() + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); checkAuthenticateAccountsPermission(account); - if (!canUserModifyAccounts(Binder.getCallingUid())) { - return false; - } + /* + * Child users are not allowed to add accounts. Only the accounts that are + * shared by the parent profile can be added to child profile. + * + * TODO: Only allow accounts that were shared to be added by + * a limited user. + */ UserAccounts accounts = getUserAccountsForCaller(); // fails if the account already exists @@ -588,12 +595,9 @@ public class AccountManagerService if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { // Create a Session for the target user and pass in the bundle completeCloningAccount(result, account, toAccounts); - } else { - clonePassword(fromAccounts, toAccounts, account); } return; } else { - clonePassword(fromAccounts, toAccounts, account); super.onResult(result); } } @@ -604,23 +608,6 @@ public class AccountManagerService return true; } - // TODO: Remove fallback - move to authenticator - private void clonePassword(UserAccounts fromAccounts, UserAccounts toAccounts, - Account account) { - long id = clearCallingIdentity(); - try { - String password = readPasswordInternal(fromAccounts, account); - String extraFlags = readUserDataInternal(fromAccounts, account, "flags"); - String extraServices = readUserDataInternal(fromAccounts, account, "services"); - Bundle extras = new Bundle(); - extras.putString("flags", extraFlags); - extras.putString("services", extraServices); - addAccountInternal(toAccounts, account, password, extras, true); - } finally { - restoreCallingIdentity(id); - } - } - void completeCloningAccount(final Bundle result, final Account account, final UserAccounts targetUser) { long id = clearCallingIdentity(); @@ -633,7 +620,17 @@ public class AccountManagerService } public void run() throws RemoteException { - mAuthenticator.addAccountFromCredentials(this, account, result); + // Confirm that the owner's account still exists before this step. + UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); + synchronized (owner.cacheLock) { + Account[] ownerAccounts = getAccounts(UserHandle.USER_OWNER); + for (Account acc : ownerAccounts) { + if (acc.equals(account)) { + mAuthenticator.addAccountFromCredentials(this, account, result); + break; + } + } + } } public void onResult(Bundle result) { @@ -706,7 +703,33 @@ public class AccountManagerService db.endTransaction(); } sendAccountsChangedBroadcast(accounts.userId); - return true; + } + if (accounts.userId == UserHandle.USER_OWNER) { + addAccountToLimitedUsers(account); + } + return true; + } + + /** + * Adds the account to all limited users as shared accounts. If the user is currently + * running, then clone the account too. + * @param account the account to share with limited users + */ + private void addAccountToLimitedUsers(Account account) { + List<UserInfo> users = getUserManager().getUsers(); + for (UserInfo user : users) { + if (user.isRestricted()) { + addSharedAccountAsUser(account, user.id); + try { + if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) { + mMessageHandler.sendMessage(mMessageHandler.obtainMessage( + MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id, + account)); + } + } catch (RemoteException re) { + // Shouldn't happen + } + } } } @@ -906,6 +929,7 @@ public class AccountManagerService } } + @Override public void invalidateAuthToken(String accountType, String authToken) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "invalidateAuthToken: accountType " + accountType @@ -1181,7 +1205,7 @@ public class AccountManagerService final int callingUid = getCallingUid(); clearCallingIdentity(); - if (callingUid != android.os.Process.SYSTEM_UID) { + if (callingUid != Process.SYSTEM_UID) { throw new SecurityException("can only call from system"); } UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); @@ -1351,7 +1375,7 @@ public class AccountManagerService String subtitle = ""; if (index > 0) { title = titleAndSubtitle.substring(0, index); - subtitle = titleAndSubtitle.substring(index + 1); + subtitle = titleAndSubtitle.substring(index + 1); } UserHandle user = new UserHandle(userId); n.setLatestEventInfo(mContext, title, subtitle, @@ -1409,7 +1433,7 @@ public class AccountManagerService return id; } - public void addAcount(final IAccountManagerResponse response, final String accountType, + public void addAccount(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -1426,8 +1450,7 @@ public class AccountManagerService checkManageAccountsPermission(); // Is user disallowed from modifying accounts? - if (getUserManager().getUserRestrictions(Binder.getCallingUserHandle()) - .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) { + if (!canUserModifyAccounts(Binder.getCallingUid())) { try { response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, "User is not allowed to add an account!"); @@ -1472,7 +1495,7 @@ public class AccountManagerService int userId) { // Only allow the system process to read accounts of other users if (userId != UserHandle.getCallingUserId() - && Binder.getCallingUid() != android.os.Process.myUid()) { + && Binder.getCallingUid() != Process.myUid()) { throw new SecurityException("User " + UserHandle.getCallingUserId() + " trying to confirm account credentials for " + userId); } @@ -1588,7 +1611,8 @@ public class AccountManagerService public void run() throws RemoteException { synchronized (mAccounts.cacheLock) { - mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid); + mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, + null); } // check whether each account matches the requested features mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); @@ -1677,7 +1701,7 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, null, callingUid); + return getAccountsFromCacheLocked(accounts, null, callingUid, null); } } finally { restoreCallingIdentity(identityToken); @@ -1718,7 +1742,7 @@ public class AccountManagerService if (userAccounts == null) continue; synchronized (userAccounts.cacheLock) { Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, - Binder.getCallingUid()); + Binder.getCallingUid(), null); for (int a = 0; a < accounts.length; a++) { runningAccounts.add(new AccountAndUser(accounts[a], userId)); } @@ -1732,10 +1756,15 @@ public class AccountManagerService @Override public Account[] getAccountsAsUser(String type, int userId) { - final int callingUid = Binder.getCallingUid(); + return getAccountsAsUser(type, userId, null, -1); + } + + private Account[] getAccountsAsUser(String type, int userId, String callingPackage, + int packageUid) { + int callingUid = Binder.getCallingUid(); // Only allow the system process to read accounts of other users if (userId != UserHandle.getCallingUserId() - && callingUid != android.os.Process.myUid()) { + && callingUid != Process.myUid()) { throw new SecurityException("User " + UserHandle.getCallingUserId() + " trying to get account for " + userId); } @@ -1745,12 +1774,17 @@ public class AccountManagerService + ", caller's uid " + Binder.getCallingUid() + ", pid " + Binder.getCallingPid()); } + // If the original calling app was using the framework account chooser activity, we'll + // be passed in the original caller's uid here, which is what should be used for filtering. + if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { + callingUid = packageUid; + } checkReadAccountsPermission(); UserAccounts accounts = getUserAccounts(userId); long identityToken = clearCallingIdentity(); try { synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, type, callingUid); + return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage); } } finally { restoreCallingIdentity(identityToken); @@ -1821,6 +1855,16 @@ public class AccountManagerService return getAccountsAsUser(type, UserHandle.getCallingUserId()); } + @Override + public Account[] getAccountsForPackage(String packageName, int uid) { + int callingUid = Binder.getCallingUid(); + if (!UserHandle.isSameApp(callingUid, Process.myUid())) { + throw new SecurityException("getAccountsForPackage() called from unauthorized uid " + + callingUid + " with uid=" + uid); + } + return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid); + } + public void getAccountsByFeatures(IAccountManagerResponse response, String type, String[] features) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -1840,7 +1884,7 @@ public class AccountManagerService if (features == null || features.length == 0) { Account[] accounts; synchronized (userAccounts.cacheLock) { - accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid); + accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); } Bundle result = new Bundle(); result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); @@ -2154,6 +2198,10 @@ public class AccountManagerService session.onTimedOut(); break; + case MESSAGE_COPY_SHARED_ACCOUNT: + copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2); + break; + default: throw new IllegalStateException("unhandled message: " + msg.what); } @@ -2363,7 +2411,7 @@ public class AccountManagerService } } else { Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, - android.os.Process.myUid()); + Process.myUid(), null); fout.println("Accounts: " + accounts.length); for (Account account : accounts) { fout.println(" " + account); @@ -2516,7 +2564,7 @@ public class AccountManagerService private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, int callerUid) { - if (callerUid == android.os.Process.SYSTEM_UID) { + if (callerUid == Process.SYSTEM_UID) { return true; } UserAccounts accounts = getUserAccountsForCaller(); @@ -2569,10 +2617,10 @@ public class AccountManagerService } private boolean canUserModifyAccounts(int callingUid) { - if (callingUid != android.os.Process.myUid()) { - Bundle restrictions = getUserManager().getUserRestrictions( - new UserHandle(UserHandle.getUserId(callingUid))); - if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) { + if (callingUid != Process.myUid()) { + if (getUserManager().getUserRestrictions( + new UserHandle(UserHandle.getUserId(callingUid))) + .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { return false; } } @@ -2583,7 +2631,7 @@ public class AccountManagerService throws RemoteException { final int callingUid = getCallingUid(); - if (callingUid != android.os.Process.SYSTEM_UID) { + if (callingUid != Process.SYSTEM_UID) { throw new SecurityException(); } @@ -2703,14 +2751,14 @@ public class AccountManagerService } private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, - int callingUid) { + int callingUid, String callingPackage) { if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 - || callingUid == android.os.Process.myUid()) { + || callingUid == Process.myUid()) { return unfiltered; } if (mUserManager.getUserInfo(userAccounts.userId).isRestricted()) { String[] packages = mPackageManager.getPackagesForUid(callingUid); - // If any of the packages includes a white listed package, return the full set, + // If any of the packages is a white listed package, return the full set, // otherwise return non-shared accounts only. // This might be a temporary way to specify a whitelist String whiteList = mContext.getResources().getString( @@ -2723,16 +2771,34 @@ public class AccountManagerService ArrayList<Account> allowed = new ArrayList<Account>(); Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; - for (Account account : unfiltered) { - boolean found = false; - for (Account shared : sharedAccounts) { - if (shared.equals(account)) { - found = true; - break; + String requiredAccountType = ""; + try { + for (String packageName : packages) { + PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); + if (pi != null && pi.restrictedAccountType != null) { + requiredAccountType = pi.restrictedAccountType; + // If it matches the package name of the original caller, use this choice. + if (callingPackage != null && packageName.equals(callingPackage)) { + break; + } } } - if (!found) { + } catch (NameNotFoundException nnfe) { + } + for (Account account : unfiltered) { + if (account.type.equals(requiredAccountType)) { allowed.add(account); + } else { + boolean found = false; + for (Account shared : sharedAccounts) { + if (shared.equals(account)) { + found = true; + break; + } + } + if (!found) { + allowed.add(account); + } } } Account[] filtered = new Account[allowed.size()]; @@ -2743,15 +2809,19 @@ public class AccountManagerService } } + /* + * packageName can be null. If not null, it should be used to filter out restricted accounts + * that the package is not allowed to access. + */ protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, - int callingUid) { + int callingUid, String callingPackage) { if (accountType != null) { final Account[] accounts = userAccounts.accountCache.get(accountType); if (accounts == null) { return EMPTY_ACCOUNT_ARRAY; } else { return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), - callingUid); + callingUid, callingPackage); } } else { int totalLength = 0; @@ -2768,7 +2838,7 @@ public class AccountManagerService accountsOfType.length); totalLength += accountsOfType.length; } - return filterSharedAccounts(userAccounts, accounts, callingUid); + return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 90a727d..7710f13 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1410,7 +1410,7 @@ public final class ActivityManagerService extends ActivityManagerNative public static void setSystemProcess() { try { ActivityManagerService m = mSelf; - + ServiceManager.addService("activity", m, true); ServiceManager.addService("meminfo", new MemBinder(m)); ServiceManager.addService("gfxinfo", new GraphicsBinder(m)); @@ -1448,6 +1448,11 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager = wm; } + public void startObservingNativeCrashes() { + final NativeCrashListener ncl = new NativeCrashListener(); + ncl.start(); + } + public static final Context main(int factoryTest) { AThread thr = new AThread(); thr.start(); @@ -8345,6 +8350,14 @@ public final class ActivityManagerService extends ActivityManagerNative final String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); + handleApplicationCrashInner(r, processName, crashInfo); + } + + /* Native crash reporting uses this inner version because it needs to be somewhat + * decoupled from the AM-managed cleanup lifecycle + */ + void handleApplicationCrashInner(ProcessRecord r, String processName, + ApplicationErrorReport.CrashInfo crashInfo) { EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(), UserHandle.getUserId(Binder.getCallingUid()), processName, r == null ? -1 : r.info.flags, @@ -8858,7 +8871,7 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } - if (!r.crashing && !r.notResponding) { + if (!r.crashing && !r.notResponding && !r.forceCrashReport) { return null; } @@ -8869,7 +8882,7 @@ public final class ActivityManagerService extends ActivityManagerNative report.time = timeMillis; report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - if (r.crashing) { + if (r.crashing || r.forceCrashReport) { report.type = ApplicationErrorReport.TYPE_CRASH; report.crashInfo = crashInfo; } else if (r.notResponding) { @@ -10879,7 +10892,7 @@ public final class ActivityManagerService extends ActivityManagerNative mProcessesToGc.remove(app); // Dismiss any open dialogs. - if (app.crashDialog != null) { + if (app.crashDialog != null && !app.forceCrashReport) { app.crashDialog.dismiss(); app.crashDialog = null; } diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java new file mode 100644 index 0000000..e83433f --- /dev/null +++ b/services/java/com/android/server/am/NativeCrashListener.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2013 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.am; + +import android.app.ApplicationErrorReport.CrashInfo; +import android.util.Slog; + +import libcore.io.ErrnoException; +import libcore.io.Libcore; +import libcore.io.StructTimeval; +import libcore.io.StructUcred; + +import static libcore.io.OsConstants.*; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.net.InetSocketAddress; +import java.net.InetUnixAddress; + +/** + * Set up a Unix domain socket that debuggerd will connect() to in + * order to write a description of a native crash. The crash info is + * then parsed and forwarded to the ActivityManagerService's normal + * crash handling code. + * + * Note that this component runs in a separate thread. + */ +class NativeCrashListener extends Thread { + static final String TAG = "NativeCrashListener"; + static final boolean DEBUG = false; + + // Must match the path defined in debuggerd.c. + static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket"; + + // Use a short timeout on socket operations and abandon the connection + // on hard errors + static final long SOCKET_TIMEOUT_MILLIS = 1000; // 1 second + + final ActivityManagerService mAm; + + /* + * Spin the actual work of handling a debuggerd crash report into a + * separate thread so that the listener can go immediately back to + * accepting incoming connections. + */ + class NativeCrashReporter extends Thread { + ProcessRecord mApp; + int mSignal; + String mCrashReport; + + NativeCrashReporter(ProcessRecord app, int signal, String report) { + super("NativeCrashReport"); + mApp = app; + mSignal = signal; + mCrashReport = report; + } + + @Override + public void run() { + try { + CrashInfo ci = new CrashInfo(); + ci.exceptionClassName = "Native crash"; + ci.exceptionMessage = Libcore.os.strsignal(mSignal); + ci.throwFileName = "unknown"; + ci.throwClassName = "unknown"; + ci.throwMethodName = "unknown"; + ci.stackTrace = mCrashReport; + + if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()"); + mAm.handleApplicationCrashInner(mApp, mApp.processName, ci); + if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned"); + } catch (Exception e) { + Slog.e(TAG, "Unable to report native crash", e); + } + } + } + + /* + * Daemon thread that accept()s incoming domain socket connections from debuggerd + * and processes the crash dump that is passed through. + */ + NativeCrashListener() { + mAm = ActivityManagerService.self(); + } + + @Override + public void run() { + final byte[] ackSignal = new byte[1]; + + if (DEBUG) Slog.i(TAG, "Starting up"); + + // The file system entity for this socket is created with 0700 perms, owned + // by system:system. debuggerd runs as root, so is capable of connecting to + // it, but 3rd party apps cannot. + { + File socketFile = new File(DEBUGGERD_SOCKET_PATH); + if (socketFile.exists()) { + socketFile.delete(); + } + } + + try { + FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0); + final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH); + Libcore.os.bind(serverFd, sockAddr, 0); + Libcore.os.listen(serverFd, 1); + + while (true) { + InetSocketAddress peer = new InetSocketAddress(); + FileDescriptor peerFd = null; + try { + if (DEBUG) Slog.v(TAG, "Waiting for debuggerd connection"); + peerFd = Libcore.os.accept(serverFd, peer); + if (DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd); + if (peerFd != null) { + // Only the superuser is allowed to talk to us over this socket + StructUcred credentials = + Libcore.os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED); + if (credentials.uid == 0) { + // the reporting thread may take responsibility for + // acking the debugger; make sure we play along. + consumeNativeCrashData(peerFd); + } + } + } catch (Exception e) { + Slog.w(TAG, "Error handling connection", e); + } finally { + // Always ack debuggerd's connection to us. The actual + // byte written is irrelevant. + if (peerFd != null) { + try { + Libcore.os.write(peerFd, ackSignal, 0, 1); + } catch (Exception e) { /* we don't care about failures here */ } + } + } + } + } catch (Exception e) { + Slog.e(TAG, "Unable to init native debug socket!", e); + } + } + + static int unpackInt(byte[] buf, int offset) { + int b0, b1, b2, b3; + + b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension + b1 = ((int) buf[offset+1]) & 0xFF; + b2 = ((int) buf[offset+2]) & 0xFF; + b3 = ((int) buf[offset+3]) & 0xFF; + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + + static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes) + throws ErrnoException { + int totalRead = 0; + while (numBytes > 0) { + int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes); + if (n <= 0) { + if (DEBUG) { + Slog.w(TAG, "Needed " + numBytes + " but saw " + n); + } + return -1; // premature EOF or timeout + } + numBytes -= n; + totalRead += n; + } + return totalRead; + } + + // Read the crash report from the debuggerd connection + void consumeNativeCrashData(FileDescriptor fd) { + if (DEBUG) Slog.i(TAG, "debuggerd connected"); + final byte[] buf = new byte[4096]; + final ByteArrayOutputStream os = new ByteArrayOutputStream(4096); + + try { + StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS); + Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout); + Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout); + + // first, the pid and signal number + int headerBytes = readExactly(fd, buf, 0, 8); + if (headerBytes != 8) { + // protocol failure; give up + Slog.e(TAG, "Unable to read from debuggerd"); + return; + } + + int pid = unpackInt(buf, 0); + int signal = unpackInt(buf, 4); + if (DEBUG) { + Slog.v(TAG, "Read pid=" + pid + " signal=" + signal); + } + + // now the text of the dump + if (pid > 0) { + final ProcessRecord pr; + synchronized (mAm.mPidsSelfLocked) { + pr = mAm.mPidsSelfLocked.get(pid); + } + if (pr != null) { + int bytes; + do { + // get some data + bytes = Libcore.os.read(fd, buf, 0, buf.length); + if (bytes > 0) { + if (DEBUG) { + String s = new String(buf, 0, bytes, "UTF-8"); + Slog.v(TAG, "READ=" + bytes + "> " + s); + } + // did we just get the EOD null byte? + if (buf[bytes-1] == 0) { + os.write(buf, 0, bytes-1); // exclude the EOD token + break; + } + // no EOD, so collect it and read more + os.write(buf, 0, bytes); + } + } while (bytes > 0); + + // Okay, we've got the report. + if (DEBUG) Slog.v(TAG, "processing"); + + // Mark the process record as being a native crash so that the + // cleanup mechanism knows we're still submitting the report + // even though the process will vanish as soon as we let + // debuggerd proceed. + synchronized (mAm) { + pr.crashing = true; + pr.forceCrashReport = true; + } + + // Crash reporting is synchronous but we want to let debuggerd + // go about it business right away, so we spin off the actual + // reporting logic on a thread and let it take it's time. + final String reportString = new String(os.toByteArray(), "UTF-8"); + (new NativeCrashReporter(pr, signal, reportString)).start(); + } else { + Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid); + } + } else { + Slog.e(TAG, "Bogus pid!"); + } + } catch (Exception e) { + Slog.e(TAG, "Exception dealing with report", e); + // ugh, fail. + } + } + +} diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index 9e25e30..1a635a9 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -144,8 +144,8 @@ class ProcessList { // These are the high-end OOM level limits. This is appropriate for a // 1280x800 or larger screen with around 1GB RAM. Values are in KB. private final long[] mOomMinFreeHigh = new long[] { - 32768, 40960, 49152, - 57344, 65536, 81920 + 49152, 61440, 73728, + 86016, 98304, 122880 }; // The actual OOM killer memory levels we are using. private final long[] mOomMinFree = new long[mOomAdj.length]; diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index a32af2f..7929f96 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -138,6 +138,7 @@ class ProcessRecord { boolean persistent; // always keep this application running? boolean crashing; // are we in the process of crashing? Dialog crashDialog; // dialog being displayed due to crash. + boolean forceCrashReport; // suppress normal auto-dismiss of crash dialog & report UI? boolean notResponding; // does the app have a not responding dialog? Dialog anrDialog; // dialog being displayed due to app not resp. boolean removed; // has app package been removed from device? diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java index 2884eaf..59403c5 100644 --- a/services/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/java/com/android/server/connectivity/Nat464Xlat.java @@ -87,6 +87,10 @@ public class Nat464Xlat extends BaseNetworkObserver { return netType == TYPE_MOBILE && !lp.hasIPv4Address(); } + public static boolean isRunningClat(LinkProperties lp) { + return lp != null && lp.getAllInterfaceNames().contains(CLAT_INTERFACE_NAME); + } + /** * Starts the clat daemon. * @param lp The link properties of the interface to start clatd on. diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 30d98eb..ca7bba2 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -110,8 +110,10 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManager; import android.os.Environment.UserEnvironment; +import android.os.UserManager; +import android.provider.Settings.Secure; +import android.security.KeyStore; import android.security.SystemKeyStore; import android.util.DisplayMetrics; import android.util.EventLog; @@ -1323,6 +1325,12 @@ public class PackageManagerService extends IPackageManager.Stub { ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL) : 0)); + // If this is the first boot, and it is a normal boot, then + // we need to initialize the default preferred apps. + if (!mRestoredSettings && !onlyCore) { + mSettings.readDefaultPreferredAppsLPw(this, 0); + } + // can downgrade to reader mSettings.writeLPr(); @@ -5114,141 +5122,90 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); } } - if (bp != null && bp.packageSetting != null) { - final String perm = bp.name; - boolean allowed; - boolean allowedSig = false; - final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; - if (level == PermissionInfo.PROTECTION_NORMAL - || level == PermissionInfo.PROTECTION_DANGEROUS) { - // If the permission is required, or it's optional and was previously - // granted to the application, then allow it. Otherwise deny. - allowed = (required || origPermissions.contains(perm)); - } else if (bp.packageSetting == null) { - // This permission is invalid; skip it. - allowed = false; - } else if (level == PermissionInfo.PROTECTION_SIGNATURE) { - allowed = (compareSignatures( - bp.packageSetting.signatures.mSignatures, pkg.mSignatures) - == PackageManager.SIGNATURE_MATCH) - || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures) - == PackageManager.SIGNATURE_MATCH); - if (!allowed && (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) { - if (isSystemApp(pkg)) { - // For updated system applications, a system permission - // is granted only if it had been defined by the original application. - if (isUpdatedSystemApp(pkg)) { - final PackageSetting sysPs = mSettings - .getDisabledSystemPkgLPr(pkg.packageName); - final GrantedPermissions origGp = sysPs.sharedUser != null - ? sysPs.sharedUser : sysPs; - if (origGp.grantedPermissions.contains(perm)) { - allowed = true; - } else { - // The system apk may have been updated with an older - // version of the one on the data partition, but which - // granted a new system permission that it didn't have - // before. In this case we do want to allow the app to - // now get the new permission, because it is allowed by - // the system image. - allowed = false; - if (sysPs.pkg != null) { - for (int j=0; - j<sysPs.pkg.requestedPermissions.size(); j++) { - if (perm.equals( - sysPs.pkg.requestedPermissions.get(j))) { - allowed = true; - break; - } - } - } - } - } else { - allowed = true; - } - } - } - if (!allowed && (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { - // For development permissions, a development permission - // is granted only if it was already granted. - allowed = origPermissions.contains(perm); - } - if (allowed) { - allowedSig = true; - } - } else { - allowed = false; + + if (bp == null || bp.packageSetting == null) { + Slog.w(TAG, "Unknown permission " + name + + " in package " + pkg.packageName); + continue; + } + + final String perm = bp.name; + boolean allowed; + boolean allowedSig = false; + final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; + if (level == PermissionInfo.PROTECTION_NORMAL + || level == PermissionInfo.PROTECTION_DANGEROUS) { + // We grant a normal or dangerous permission if any of the following + // are true: + // 1) The permission is required + // 2) The permission is optional, but was granted in the past + // 3) The permission is optional, but was requested by an + // app in /system (not /data) + // + // Otherwise, reject the permission. + allowed = (required || origPermissions.contains(perm) + || (isSystemApp(ps) && !isUpdatedSystemApp(ps))); + } else if (bp.packageSetting == null) { + // This permission is invalid; skip it. + allowed = false; + } else if (level == PermissionInfo.PROTECTION_SIGNATURE) { + allowed = grantSignaturePermission(perm, pkg, bp, origPermissions); + if (allowed) { + allowedSig = true; + } + } else { + allowed = false; + } + if (DEBUG_INSTALL) { + if (gp != ps) { + Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); } - if (DEBUG_INSTALL) { - if (gp != ps) { - Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); + } + if (allowed) { + if (!isSystemApp(ps) && ps.permissionsFixed) { + // If this is an existing, non-system package, then + // we can't add any new permissions to it. + if (!allowedSig && !gp.grantedPermissions.contains(perm)) { + // Except... if this is a permission that was added + // to the platform (note: need to only do this when + // updating the platform). + allowed = isNewPlatformPermissionForPackage(perm, pkg); } } if (allowed) { - if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 - && ps.permissionsFixed) { - // If this is an existing, non-system package, then - // we can't add any new permissions to it. - if (!allowedSig && !gp.grantedPermissions.contains(perm)) { - allowed = false; - // Except... if this is a permission that was added - // to the platform (note: need to only do this when - // updating the platform). - final int NP = PackageParser.NEW_PERMISSIONS.length; - for (int ip=0; ip<NP; ip++) { - final PackageParser.NewPermissionInfo npi - = PackageParser.NEW_PERMISSIONS[ip]; - if (npi.name.equals(perm) - && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { - allowed = true; - Log.i(TAG, "Auto-granting " + perm + " to old pkg " - + pkg.packageName); - break; - } - } - } - } - if (allowed) { - if (!gp.grantedPermissions.contains(perm)) { - changedPermission = true; - gp.grantedPermissions.add(perm); - gp.gids = appendInts(gp.gids, bp.gids); - } else if (!ps.haveGids) { - gp.gids = appendInts(gp.gids, bp.gids); - } - } else { - Slog.w(TAG, "Not granting permission " + perm - + " to package " + pkg.packageName - + " because it was previously installed without"); - } - } else { - if (gp.grantedPermissions.remove(perm)) { + if (!gp.grantedPermissions.contains(perm)) { changedPermission = true; - gp.gids = removeInts(gp.gids, bp.gids); - Slog.i(TAG, "Un-granting permission " + perm - + " from package " + pkg.packageName - + " (protectionLevel=" + bp.protectionLevel - + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) - + ")"); - } else { - Slog.w(TAG, "Not granting permission " + perm - + " to package " + pkg.packageName - + " (protectionLevel=" + bp.protectionLevel - + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) - + ")"); + gp.grantedPermissions.add(perm); + gp.gids = appendInts(gp.gids, bp.gids); + } else if (!ps.haveGids) { + gp.gids = appendInts(gp.gids, bp.gids); } + } else { + Slog.w(TAG, "Not granting permission " + perm + + " to package " + pkg.packageName + + " because it was previously installed without"); } } else { - Slog.w(TAG, "Unknown permission " + name - + " in package " + pkg.packageName); + if (gp.grantedPermissions.remove(perm)) { + changedPermission = true; + gp.gids = removeInts(gp.gids, bp.gids); + Slog.i(TAG, "Un-granting permission " + perm + + " from package " + pkg.packageName + + " (protectionLevel=" + bp.protectionLevel + + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + + ")"); + } else { + Slog.w(TAG, "Not granting permission " + perm + + " to package " + pkg.packageName + + " (protectionLevel=" + bp.protectionLevel + + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + + ")"); + } } } if ((changedPermission || replace) && !ps.permissionsFixed && - ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) || - ((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){ + !isSystemApp(ps) || isUpdatedSystemApp(ps)){ // This is the first that we have heard about this package, so the // permissions we have now selected are fixed until explicitly // changed. @@ -5256,7 +5213,77 @@ public class PackageManagerService extends IPackageManager.Stub { } ps.haveGids = true; } - + + private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) { + boolean allowed = false; + final int NP = PackageParser.NEW_PERMISSIONS.length; + for (int ip=0; ip<NP; ip++) { + final PackageParser.NewPermissionInfo npi + = PackageParser.NEW_PERMISSIONS[ip]; + if (npi.name.equals(perm) + && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { + allowed = true; + Log.i(TAG, "Auto-granting " + perm + " to old pkg " + + pkg.packageName); + break; + } + } + return allowed; + } + + private boolean grantSignaturePermission(String perm, PackageParser.Package pkg, + BasePermission bp, HashSet<String> origPermissions) { + boolean allowed; + allowed = (compareSignatures( + bp.packageSetting.signatures.mSignatures, pkg.mSignatures) + == PackageManager.SIGNATURE_MATCH) + || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures) + == PackageManager.SIGNATURE_MATCH); + if (!allowed && (bp.protectionLevel + & PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) { + if (isSystemApp(pkg)) { + // For updated system applications, a system permission + // is granted only if it had been defined by the original application. + if (isUpdatedSystemApp(pkg)) { + final PackageSetting sysPs = mSettings + .getDisabledSystemPkgLPr(pkg.packageName); + final GrantedPermissions origGp = sysPs.sharedUser != null + ? sysPs.sharedUser : sysPs; + if (origGp.grantedPermissions.contains(perm)) { + allowed = true; + } else { + // The system apk may have been updated with an older + // version of the one on the data partition, but which + // granted a new system permission that it didn't have + // before. In this case we do want to allow the app to + // now get the new permission, because it is allowed by + // the system image. + allowed = false; + if (sysPs.pkg != null) { + for (int j=0; + j<sysPs.pkg.requestedPermissions.size(); j++) { + if (perm.equals( + sysPs.pkg.requestedPermissions.get(j))) { + allowed = true; + break; + } + } + } + } + } else { + allowed = true; + } + } + } + if (!allowed && (bp.protectionLevel + & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { + // For development permissions, a development permission + // is granted only if it was already granted. + allowed = origPermissions.contains(perm); + } + return allowed; + } + final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, @@ -8361,6 +8388,10 @@ public class PackageManagerService extends IPackageManager.Stub { return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0; } + private static boolean isUpdatedSystemApp(PackageSetting ps) { + return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; + } + private static boolean isUpdatedSystemApp(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } @@ -8616,6 +8647,17 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeLPr(); } } + // A user ID was deleted here. Go through all users and remove it from + // KeyStore. + final int appId = outInfo.removedAppId; + if (appId != -1) { + final KeyStore keyStore = KeyStore.getInstance(); + if (keyStore != null) { + for (final int userId : sUserManager.getUserIds()) { + keyStore.clearUid(UserHandle.getUid(userId, appId)); + } + } + } } /* @@ -8735,7 +8777,7 @@ public class PackageManagerService extends IPackageManager.Stub { false, //installed true, //stopped true, //notLaunched - null, null); + null, null, null); if (!isSystemApp(ps)) { if (ps.isAnyInstalled(sUserManager.getUserIds())) { // Other user still have this package installed, so all @@ -9308,9 +9350,12 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void setApplicationEnabledSetting(String appPackageName, - int newState, int flags, int userId) { + int newState, int flags, int userId, String callingPackage) { if (!sUserManager.exists(userId)) return; - setEnabledSetting(appPackageName, null, newState, flags, userId); + if (callingPackage == null) { + callingPackage = Integer.toString(Binder.getCallingUid()); + } + setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage); } @Override @@ -9318,11 +9363,11 @@ public class PackageManagerService extends IPackageManager.Stub { int newState, int flags, int userId) { if (!sUserManager.exists(userId)) return; setEnabledSetting(componentName.getPackageName(), - componentName.getClassName(), newState, flags, userId); + componentName.getClassName(), newState, flags, userId, null); } - private void setEnabledSetting( - final String packageName, String className, int newState, final int flags, int userId) { + private void setEnabledSetting(final String packageName, String className, int newState, + final int flags, int userId, String callingPackage) { if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT || newState == COMPONENT_ENABLED_STATE_ENABLED || newState == COMPONENT_ENABLED_STATE_DISABLED @@ -9368,7 +9413,12 @@ public class PackageManagerService extends IPackageManager.Stub { // Nothing to do return; } - pkgSetting.setEnabled(newState, userId); + if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + // Don't care about who enables an app. + callingPackage = null; + } + pkgSetting.setEnabled(newState, userId, callingPackage); // pkgSetting.pkg.mSetEnabled = newState; } else { // We're dealing with a component level state change diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java index ae1b213..e64ec6d 100644 --- a/services/java/com/android/server/pm/PackageSettingBase.java +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -189,14 +189,20 @@ class PackageSettingBase extends GrantedPermissions { return DEFAULT_USER_STATE; } - void setEnabled(int state, int userId) { - modifyUserState(userId).enabled = state; + void setEnabled(int state, int userId, String callingPackage) { + PackageUserState st = modifyUserState(userId); + st.enabled = state; + st.lastDisableAppCaller = callingPackage; } int getEnabled(int userId) { return readUserState(userId).enabled; } + String getLastDisabledAppCaller(int userId) { + return readUserState(userId).lastDisableAppCaller; + } + void setInstalled(boolean inst, int userId) { modifyUserState(userId).installed = inst; } @@ -249,13 +255,14 @@ class PackageSettingBase extends GrantedPermissions { } void setUserState(int userId, int enabled, boolean installed, boolean stopped, - boolean notLaunched, HashSet<String> enabledComponents, + boolean notLaunched, String lastDisableAppCaller, HashSet<String> enabledComponents, HashSet<String> disabledComponents) { PackageUserState state = modifyUserState(userId); state.enabled = enabled; state.installed = installed; state.stopped = stopped; state.notLaunched = notLaunched; + state.lastDisableAppCaller = lastDisableAppCaller; state.enabledComponents = enabledComponents; state.disabledComponents = disabledComponents; } diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java index 15d2a5a..4bbdb5e 100644 --- a/services/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/java/com/android/server/pm/SELinuxMMAC.java @@ -206,10 +206,10 @@ public final class SELinuxMMAC { String tagName = parser.getName(); if ("seinfo".equals(tagName)) { String seinfoValue = parser.getAttributeValue(null, "value"); - if (seinfoValue != null) { + if (validateValue(seinfoValue)) { seinfo = seinfoValue; } else { - Slog.w(TAG, "<seinfo> without value at " + Slog.w(TAG, "<seinfo> without valid value at " + parser.getPositionDescription()); } } @@ -219,6 +219,28 @@ public final class SELinuxMMAC { } /** + * General validation routine for tag values. + * Returns a boolean indicating if the passed string + * contains only letters or underscores. + */ + private static boolean validateValue(String name) { + if (name == null) + return false; + + final int N = name.length(); + if (N == 0) + return false; + + for (int i = 0; i < N; i++) { + final char c = name.charAt(i); + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c != '_')) { + return false; + } + } + return true; + } + + /** * Labels a package based on an seinfo tag from install policy. * The label is attached to the ApplicationInfo instance of the package. * @param PackageParser.Package object representing the package diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index e645078..2e48074 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -104,6 +104,7 @@ final class Settings { private static final String ATTR_CODE = "code"; private static final String ATTR_NOT_LAUNCHED = "nl"; private static final String ATTR_ENABLED = "enabled"; + private static final String ATTR_ENABLED_CALLER = "enabledCaller"; private static final String ATTR_STOPPED = "stopped"; private static final String ATTR_INSTALLED = "inst"; @@ -453,7 +454,7 @@ final class Settings { installed, true, // stopped, true, // notLaunched - null, null); + null, null, null); writePackageRestrictionsLPr(user.id); } } @@ -850,7 +851,7 @@ final class Settings { true, // installed false, // stopped false, // notLaunched - null, null); + null, null, null); } return; } @@ -895,6 +896,8 @@ final class Settings { final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); final int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr); + final String enabledCaller = parser.getAttributeValue(null, + ATTR_ENABLED_CALLER); final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED); final boolean installed = installedStr == null ? true : Boolean.parseBoolean(installedStr); @@ -925,7 +928,7 @@ final class Settings { } ps.setUserState(userId, enabled, installed, stopped, notLaunched, - enabledComponents, disabledComponents); + enabledCaller, enabledComponents, disabledComponents); } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLPw(parser, userId); } else { @@ -1052,6 +1055,10 @@ final class Settings { if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { serializer.attribute(null, ATTR_ENABLED, Integer.toString(ustate.enabled)); + if (ustate.lastDisableAppCaller != null) { + serializer.attribute(null, ATTR_ENABLED_CALLER, + ustate.lastDisableAppCaller); + } } if (ustate.enabledComponents != null && ustate.enabledComponents.size() > 0) { @@ -1365,6 +1372,7 @@ final class Settings { // userId - application-specific user id // debugFlag - 0 or 1 if the package is debuggable. // dataPath - path to package's data path + // seinfo - seinfo label for the app (assigned at install time) // // NOTE: We prefer not to expose all ApplicationInfo flags for now. // @@ -1378,6 +1386,8 @@ final class Settings { sb.append((int)ai.uid); sb.append(isDebug ? " 1 " : " 0 "); sb.append(dataPath); + sb.append(" "); + sb.append(ai.seinfo); sb.append("\n"); str.write(sb.toString().getBytes()); } @@ -1593,9 +1603,6 @@ final class Settings { mReadMessages.append("No settings file found\n"); PackageManagerService.reportSettingsProblem(Log.INFO, "No settings file; creating initial state"); - if (!onlyCore) { - readDefaultPreferredAppsLPw(service, 0); - } mInternalSdkPlatform = mExternalSdkPlatform = sdkVersion; return false; } @@ -2239,14 +2246,14 @@ final class Settings { final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { try { - packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */); + packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */, null); } catch (NumberFormatException e) { if (enabledStr.equalsIgnoreCase("true")) { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0, null); } else if (enabledStr.equalsIgnoreCase("false")) { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null); } else if (enabledStr.equalsIgnoreCase("default")) { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null); } else { PackageManagerService.reportSettingsProblem(Log.WARN, "Error in package manager settings: package " + name @@ -2255,7 +2262,7 @@ final class Settings { } } } else { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null); } final String installStatusStr = parser.getAttributeValue(null, "installStatus"); @@ -2789,6 +2796,11 @@ final class Settings { pw.print(ps.getNotLaunched(user.id)); pw.print(" enabled="); pw.println(ps.getEnabled(user.id)); + String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id); + if (lastDisabledAppCaller != null) { + pw.print(prefix); pw.print(" lastDisabledCaller: "); + pw.println(lastDisabledAppCaller); + } HashSet<String> cmp = ps.getDisabledComponents(user.id); if (cmp != null && cmp.size() > 0) { pw.print(prefix); pw.println(" disabledComponents:"); diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index fecc2df..aa1b2ff 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -26,7 +26,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.RestrictionEntry; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; @@ -619,6 +618,10 @@ public class UserManagerService extends IUserManager.Stub { writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS); writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS); writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION); + writeBoolean(serializer, restrictions, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); serializer.endTag(null, TAG_RESTRICTIONS); } serializer.endTag(null, TAG_USER); @@ -735,6 +738,10 @@ public class UserManagerService extends IUserManager.Stub { readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS); readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS); readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION); + readBoolean(parser, restrictions, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); + readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); } } } @@ -1072,12 +1079,12 @@ public class UserManagerService extends IUserManager.Stub { for (RestrictionEntry entry : entries) { serializer.startTag(null, TAG_ENTRY); - serializer.attribute(null, ATTR_KEY, entry.key); - if (entry.getStringValue() != null || entry.getMultipleValues() == null) { - String value = entry.getStringValue(); + serializer.attribute(null, ATTR_KEY, entry.getKey()); + if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) { + String value = entry.getSelectedString(); serializer.text(value != null ? value : ""); } else { - String[] values = entry.getMultipleValues(); + String[] values = entry.getAllSelectedStrings(); serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length)); for (String value : values) { serializer.startTag(null, TAG_VALUE); diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java index b065310..d603cfa 100644 --- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java +++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java @@ -102,6 +102,7 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { Slog.i(TAG, "Found new update, installing..."); install(altContent, altVersion); Slog.i(TAG, "Installation successful"); + postInstall(context, intent); } } catch (Exception e) { Slog.e(TAG, "Could not update content!", e); @@ -257,4 +258,7 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { writeUpdate(updateDir, updateContent, content); writeUpdate(updateDir, updateVersion, Long.toString(version).getBytes()); } + + protected void postInstall(Context context, Intent intent) { + } } diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java new file mode 100644 index 0000000..748849e --- /dev/null +++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 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.updates; + +import android.content.Context; +import android.content.Intent; +import android.os.SELinux; +import android.provider.Settings; +import android.util.Base64; +import android.util.Slog; + +import java.io.IOException; + +public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { + + public SELinuxPolicyInstallReceiver() { + super("/data/security/", "sepolicy", "metadata/", "version"); + } + + @Override + protected void install(byte[] encodedContent, int version) throws IOException { + super.install(Base64.decode(encodedContent, Base64.DEFAULT), version); + } + + @Override + protected void postInstall(Context context, Intent intent) { + boolean mode = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.SELINUX_STATUS, 0) == 1; + SELinux.setSELinuxEnforce(mode); + } +} diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java index f9aaa17..9b5b312 100644 --- a/services/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/java/com/android/server/usb/UsbSettingsManager.java @@ -34,6 +34,7 @@ import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.Binder; import android.os.Environment; +import android.os.Process; import android.os.UserHandle; import android.util.AtomicFile; import android.util.Log; @@ -853,21 +854,29 @@ class UsbSettingsManager { public boolean hasPermission(UsbDevice device) { synchronized (mLock) { + int uid = Binder.getCallingUid(); + if (uid == Process.SYSTEM_UID) { + return true; + } SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); if (uidList == null) { return false; } - return uidList.get(Binder.getCallingUid()); + return uidList.get(uid); } } public boolean hasPermission(UsbAccessory accessory) { synchronized (mLock) { + int uid = Binder.getCallingUid(); + if (uid == Process.SYSTEM_UID) { + return true; + } SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); if (uidList == null) { return false; } - return uidList.get(Binder.getCallingUid()); + return uidList.get(uid); } } diff --git a/services/java/com/android/server/wifi/README.txt b/services/java/com/android/server/wifi/README.txt index c03bff5..39e1475 100644 --- a/services/java/com/android/server/wifi/README.txt +++ b/services/java/com/android/server/wifi/README.txt @@ -10,3 +10,10 @@ WifiNotificationController: Controls whether the open network notification is di WifiStateMachine: Tracks the various states on STA and AP connectivity and handles bring up and shut down. + +Feature description: + +Scan-only mode with Wi-Fi turned off: + - Setup wizard opts user into allowing scanning for improved location. We show no further dialogs in setup wizard since the user has just opted into the feature. This is the reason WifiService listens to DEVICE_PROVISIONED setting. + - Once the user has his device provisioned, turning off Wi-Fi from settings or from a third party app will show up a dialog reminding the user that scan mode will be on even though Wi-Fi is being turned off. The user has the choice to turn this notification off. + - In the scan mode, the device continues to allow scanning from any app with Wi-Fi turned off. This is done by disabling all networks and allowing only scans to be passed. diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index 9dde58f..f8d5d2e 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -58,6 +58,7 @@ import java.net.InetAddress; import java.net.Inet4Address; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import com.android.internal.R; import com.android.internal.app.IBatteryStats; @@ -101,6 +102,9 @@ public final class WifiService extends IWifiManager.Stub { private int mMulticastEnabled; private int mMulticastDisabled; + private AtomicBoolean mDeviceProvisioned = new AtomicBoolean(); + private AtomicBoolean mNotifyScanMode = new AtomicBoolean(); + private final IBatteryStats mBatteryStats; private final AppOpsManager mAppOps; @@ -245,6 +249,8 @@ public final class WifiService extends IWifiManager.Stub { mWifiController.start(); registerForScanModeChange(); + registerForDeviceProvisionedChange(); + registerForNotifyUserOnScanModeChange(); mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -399,10 +405,8 @@ public final class WifiService extends IWifiManager.Stub { /* Turning off Wi-Fi when scans are still available */ if (!enable && isScanningAlwaysAvailable()) { - /* This can be changed by user in the app to not be notified again */ - boolean notifyUser = (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1); - if (notifyUser) { + /* Notify if device is provisioned and user has not opted out of the notification */ + if (mNotifyScanMode.get() && mDeviceProvisioned.get()) { Intent intent = new Intent(WifiManager.ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE); mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); } @@ -875,6 +879,51 @@ public final class WifiService extends IWifiManager.Stub { false, contentObserver); } + private void getPersistedDeviceProvisioned() { + mDeviceProvisioned.set(Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0) != 0); + } + + private void getPersistedNotifyScanMode() { + mNotifyScanMode.set(Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1); + } + + /** + * Observes settings changes to notify the user when scan mode is active and + * Wi-Fi is turned off + */ + private void registerForNotifyUserOnScanModeChange() { + ContentObserver contentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + getPersistedNotifyScanMode(); + } + }; + + getPersistedNotifyScanMode(); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE), + false, contentObserver); + } + + /* + * Observes settings changes device provisioned status + */ + private void registerForDeviceProvisionedChange() { + ContentObserver contentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + getPersistedDeviceProvisioned(); + } + }; + + getPersistedDeviceProvisioned(); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), + false, contentObserver); + } + private void registerForBroadcasts() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_SCREEN_ON); @@ -899,6 +948,8 @@ public final class WifiService extends IWifiManager.Stub { pw.println("Stay-awake conditions: " + Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0)); + pw.println("mDeviceProvisioned " + mDeviceProvisioned.get()); + pw.println("mNotifyScanMode " + mNotifyScanMode.get()); pw.println("mMulticastEnabled " + mMulticastEnabled); pw.println("mMulticastDisabled " + mMulticastDisabled); mWifiController.dump(fd, pw, args); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 34052f3..cbc42eb 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5803,6 +5803,19 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public void removeRotationWatcher(IRotationWatcher watcher) { + final IBinder watcherBinder = watcher.asBinder(); + synchronized (mWindowMap) { + for (int i=0; i<mRotationWatchers.size(); i++) { + if (watcherBinder == mRotationWatchers.get(i).asBinder()) { + mRotationWatchers.remove(i); + i--; + } + } + } + } + /** * Apps that use the compact menu panel (as controlled by the panelMenuIsCompact * theme attribute) on devices that feature a physical options menu key attempt to position |