field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int required = 16843406; // 0x101028e
+ field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
field public static final int resizeMode = 16843619; // 0x1010363
method public abstract java.lang.String[] fileList();
method public abstract android.content.Context getApplicationContext();
method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
+ method public java.util.List<android.content.RestrictionEntry> getApplicationRestrictions();
method public abstract android.content.res.AssetManager getAssets();
method public abstract java.io.File getCacheDir();
method public abstract java.lang.ClassLoader getClassLoader();
field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
field public static final java.lang.String ACTION_FACTORY_TEST = "android.intent.action.FACTORY_TEST";
field public static final java.lang.String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";
+ field public static final java.lang.String ACTION_GET_RESTRICTION_ENTRIES = "android.intent.action.GET_RESTRICTION_ENTRIES";
field public static final java.lang.String ACTION_GTALK_SERVICE_CONNECTED = "android.intent.action.GTALK_CONNECTED";
field public static final java.lang.String ACTION_GTALK_SERVICE_DISCONNECTED = "android.intent.action.GTALK_DISCONNECTED";
field public static final java.lang.String ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG";
field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING";
+ field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
ctor public ReceiverCallNotAllowedException(java.lang.String);
}
+ public class RestrictionEntry implements android.os.Parcelable {
+ ctor public RestrictionEntry(java.lang.String, java.lang.String);
+ ctor public RestrictionEntry(java.lang.String, boolean);
+ ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
+ ctor public RestrictionEntry(android.os.Parcel);
+ method public int describeContents();
+ method public boolean getBooleanValue();
+ method public java.lang.String[] getMultipleValues();
+ method public java.lang.String getStringValue();
+ method public void setMultipleValues(java.lang.String[]);
+ method public void setValue(java.lang.String);
+ method public void setValue(boolean);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int TYPE_BOOLEAN = 1; // 0x1
+ field public static final int TYPE_CHOICE = 2; // 0x2
+ field public static final int TYPE_CHOICE_LEVEL = 3; // 0x3
+ field public static final int TYPE_MULTI_SELECT = 4; // 0x4
+ field public static final int TYPE_NULL = 0; // 0x0
+ field public java.lang.String[] choices;
+ field public java.lang.String description;
+ field public java.lang.String key;
+ field public java.lang.String title;
+ field public int type;
+ field public java.lang.String[] values;
+ }
+
public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
ctor public SearchRecentSuggestionsProvider();
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
public static final int ERROR_CODE_BAD_REQUEST = 8;
+ /** @hide */
+ public static final int ERROR_CODE_USER_RESTRICTED = 100;
+
/**
* Bundle key used for the {@link String} account name in results
* from methods which return information about a particular account.
}
public void onError(int code, String message) {
- if (code == ERROR_CODE_CANCELED) {
+ if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED) {
// the authenticator indicated that this request was canceled, do so now
cancel(true /* mayInterruptIfRunning */);
return;
package android.app;
import java.util.ArrayList;
+import java.util.List;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.RestrictionEntry;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.UserManager;
/**
* Base class for those who need to maintain global application state. You can
}
}
+ public List<RestrictionEntry> getApplicationRestrictions() {
+ return ((UserManager) getSystemService(USER_SERVICE))
+ .getApplicationRestrictions(getPackageName(), android.os.Process.myUserHandle());
+ }
+
public void registerComponentCallbacks(ComponentCallbacks callback) {
synchronized (mComponentCallbacks) {
mComponentCallbacks.add(callback);
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
/**
* Interface to global information about an application environment. This is
public abstract Context getApplicationContext();
/**
+ * Returns the list of restrictions for the application, or null if there are no
+ * restrictions.
+ * @return
+ */
+ public List<RestrictionEntry> getApplicationRestrictions() {
+ return getApplicationContext().getApplicationRestrictions();
+ }
+
+ /**
* Add a new {@link ComponentCallbacks} to the base application of the
* Context, which will be called at the same times as the ComponentCallbacks
* methods of activities and other components are called. Note that you
"android.intent.action.PRE_BOOT_COMPLETED";
/**
+ * Broadcast to a specific application to query any supported restrictions to impose
+ * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS}
+ * which is of type <code>ArrayList<RestrictionEntry></code>.
+ * @see RestrictionEntry
+ */
+ public static final String ACTION_GET_RESTRICTION_ENTRIES =
+ "android.intent.action.GET_RESTRICTION_ENTRIES";
+
+ /**
* Sent the first time a user is starting, to allow system apps to
* perform one time initialization. (This will not be seen by third
* party applications because a newly initialized user does not have any
public static final String EXTRA_USER_HANDLE =
"android.intent.extra.user_handle";
+ /**
+ * Extra used in the response from a BroadcastReceiver that handles
+ * {@link #ACTION_GET_RESTRICTION_ENTRIES}.
+ */
+ public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
--- /dev/null
+/*
+**
+** Copyright 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 android.content;
+
+parcelable RestrictionEntry;
--- /dev/null
+/*
+ * 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 android.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Inherited;
+
+/**
+ * Applications can expose restrictions for a restricted user on a
+ * multiuser device. The administrator can configure these restrictions that will then be
+ * applied to the restricted user. Each RestrictionsEntry is one configurable restriction.
+ * <p/>
+ * Any application that chooses to expose such restrictions does so by implementing a
+ * receiver that handles the {@link Intent.ACTION_GET_RESTRICTION_ENTRIES} action.
+ * The receiver then returns a result bundle that contains an entry called "restrictions", whose
+ * value is an ArrayList<RestrictionsEntry>.
+ */
+public class RestrictionEntry implements Parcelable {
+
+ /**
+ * A type of restriction. Use this one for information that needs to be transferred across
+ * but shouldn't be presented to the user in the UI.
+ */
+ public static final int TYPE_NULL = 0;
+ /**
+ * A type of restriction. Use this for storing true/false values, typically presented as
+ * a checkbox in the UI.
+ */
+ public static final int TYPE_BOOLEAN = 1;
+ /**
+ * A type of restriction. Use this for storing a string value, typically presented as
+ * a single-select list. The {@link #values} and {@link #choices} need to have the list of
+ * possible values and the corresponding localized strings, respectively, to present in the UI.
+ */
+ public static final int TYPE_CHOICE = 2;
+ /**
+ * A type of restriction. Use this for storing a string value, typically presented as
+ * a single-select list. The {@link #values} and {@link #choices} need to have the list of
+ * possible values and the corresponding localized strings, respectively, to present in the UI.
+ * The presentation could imply that values in lower array indices are included when a
+ * particular value is chosen.
+ */
+ public static final int TYPE_CHOICE_LEVEL = 3;
+ /**
+ * A type of restriction. Use this for presenting a multi-select list where more than one
+ * entry can be selected, such as for choosing specific titles to white-list.
+ * The {@link #values} and {@link #choices} need to have the list of
+ * possible values and the corresponding localized strings, respectively, to present in the UI.
+ * Use {@link #getMultipleValues()} and {@link #setMultipleValues(String[])} to manipulate
+ * the selections.
+ */
+ public static final int TYPE_MULTI_SELECT = 4;
+
+ /** The type of restriction. */
+ public int type;
+
+ /** The unique key that identifies the restriction. */
+ public String key;
+
+ /** The user-visible title of the restriction. */
+ public String title;
+
+ /** The user-visible secondary description of the restriction. */
+ public String description;
+
+ /** The user-visible set of choices used for single-select and multi-select lists. */
+ public String [] choices;
+
+ /** The values corresponding to the user-visible choices. The value(s) of this entry will
+ * one or more of these, returned by {@link #getMultipleValues()} and
+ * {@link #getStringValue()}.
+ */
+ public String [] values;
+
+ /* The chosen value, whose content depends on the type of the restriction. */
+ private String currentValue;
+ /* List of selected choices in the multi-select case. */
+ private String[] currentValues;
+
+ /**
+ * Constructor for {@link #TYPE_CHOICE} and {@link #TYPE_CHOICE_LEVEL} types.
+ * @param key the unique key for this restriction
+ * @param value the current value
+ */
+ public RestrictionEntry(String key, String value) {
+ this.key = key;
+ this.currentValue = value;
+ }
+
+ /**
+ * Constructor for {@link #TYPE_BOOLEAN} type.
+ * @param key the unique key for this restriction
+ * @param value the current value
+ */
+ public RestrictionEntry(String key, boolean value) {
+ this.key = key;
+ setValue(value);
+ }
+
+ /**
+ * Constructor for {@link #TYPE_MULTI_SELECT} type.
+ * @param key the unique key for this restriction
+ * @param multipleValues the list of values that are currently selected
+ */
+ public RestrictionEntry(String key, String[] multipleValues) {
+ this.key = key;
+ this.currentValues = multipleValues;
+ }
+
+ /**
+ * Returns the current value. Null for {@link #TYPE_MULTI_SELECT} type.
+ * @return the current value
+ */
+ public String getStringValue() {
+ return currentValue;
+ }
+
+ /**
+ * Returns the list of current selections. Null if the type is not {@link #TYPE_MULTI_SELECT}.
+ * @return the list of current selections.
+ */
+ public String[] getMultipleValues() {
+ return currentValues;
+ }
+
+ /**
+ * Returns the current boolean value for entries of type {@link #TYPE_BOOLEAN}.
+ * @return the current value
+ */
+ public boolean getBooleanValue() {
+ return Boolean.parseBoolean(currentValue);
+ }
+
+ /**
+ * Set the current string value.
+ * @param s the current value
+ */
+ public void setValue(String s) {
+ currentValue = s;
+ }
+
+ /**
+ * Sets the current boolean value.
+ * @param b the current value
+ */
+ public void setValue(boolean b) {
+ currentValue = Boolean.toString(b);
+ }
+
+ /**
+ * Sets the current list of selected values.
+ * @param values the current list of selected values
+ */
+ public void setMultipleValues(String[] values) {
+ currentValues = values;
+ }
+
+ private boolean equalArrays(String[] one, String[] other) {
+ if (one.length != other.length) return false;
+ for (int i = 0; i < one.length; i++) {
+ if (!one[i].equals(other[i])) return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof RestrictionEntry)) return false;
+ final RestrictionEntry other = (RestrictionEntry) o;
+ // Make sure that either currentValue matches or currentValues matches.
+ return type == other.type && key.equals(other.key)
+ &&
+ ((currentValues == null && other.currentValues == null
+ && currentValue != null && currentValue.equals(other.currentValue))
+ ||
+ (currentValue == null && other.currentValue == null
+ && currentValues != null && equalArrays(currentValues, other.currentValues)));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + key.hashCode();
+ if (currentValue != null) {
+ result = 31 * result + currentValue.hashCode();
+ } else if (currentValues != null) {
+ for (String value : currentValues) {
+ if (value != null) {
+ result = 31 * result + value.hashCode();
+ }
+ }
+ }
+ return result;
+ }
+
+ private String[] readArray(Parcel in) {
+ int count = in.readInt();
+ String[] values = new String[count];
+ for (int i = 0; i < count; i++) {
+ values[i] = in.readString();
+ }
+ return values;
+ }
+
+ public RestrictionEntry(Parcel in) {
+ type = in.readInt();
+ key = in.readString();
+ title = in.readString();
+ description = in.readString();
+ choices = readArray(in);
+ values = readArray(in);
+ currentValue = in.readString();
+ currentValues = readArray(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private void writeArray(Parcel dest, String[] values) {
+ if (values == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(values.length);
+ for (int i = 0; i < values.length; i++) {
+ dest.writeString(values[i]);
+ }
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(type);
+ dest.writeString(key);
+ dest.writeString(title);
+ dest.writeString(description);
+ writeArray(dest, choices);
+ writeArray(dest, values);
+ dest.writeString(currentValue);
+ writeArray(dest, currentValues);
+ }
+
+ public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
+ public RestrictionEntry createFromParcel(Parcel source) {
+ return new RestrictionEntry(source);
+ }
+
+ public RestrictionEntry[] newArray(int size) {
+ return new RestrictionEntry[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "RestrictionsEntry {type=" + type + ", key=" + key + ", value=" + currentValue + "}";
+ }
+}
* @hide
*/
public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
-
+
+ /** @hide */
+ public boolean requiredForAllUsers;
+
public PackageInfo() {
}
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
dest.writeInt(installLocation);
+ dest.writeInt(requiredForAllUsers ? 1 : 0);
}
public static final Parcelable.Creator<PackageInfo> CREATOR
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
installLocation = source.readInt();
+ requiredForAllUsers = source.readInt() != 0;
}
}
pi.sharedUserLabel = p.mSharedUserLabel;
pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
+ pi.requiredForAllUsers = p.mRequiredForAllUsers;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
false)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
+ false)) {
+ owner.mRequiredForAllUsers = true;
+ }
}
if (sa.getBoolean(
public int installLocation;
+ /* An app that's required for all users and cannot be uninstalled for a user */
+ public boolean mRequiredForAllUsers;
+
/**
* Digest suitable for comparing whether this package's manifest is the
* same as another.
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.pm.UserInfo;
+import android.content.RestrictionEntry;
import android.graphics.Bitmap;
/**
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
void setUserRestrictions(in Bundle restrictions, int userHandle);
+ void setApplicationRestrictions(in String packageName, in List<RestrictionEntry> entries,
+ int userHandle);
+ List<RestrictionEntry> getApplicationRestrictions(in String packageName, int userHandle);
}
import android.app.ActivityManagerNative;
import android.content.Context;
+import android.content.RestrictionEntry;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
public boolean isUserAGoat() {
return false;
}
-
+
+ /**
+ * @hide
+ */
+ public boolean isUserRestricted() {
+ try {
+ return mService.getUserInfo(getUserHandle()).isRestricted();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not check if user restricted ", re);
+ return false;
+ }
+ }
+
/**
* Return whether the given user is actively running. This means that
* the user is in the "started" state, not "stopped" -- it is currently
}
/**
- * Returns whether the current user is allow to toggle location sharing settings.
+ * Returns whether the current user is allowed to toggle location sharing settings.
* @hide
*/
public boolean isLocationSharingToggleAllowed() {
return getUserRestrictions().getBoolean(ALLOW_CONFIG_LOCATION_ACCESS);
}
+
+ /**
+ * @hide
+ */
+ public List<RestrictionEntry> getApplicationRestrictions(String packageName, UserHandle user) {
+ try {
+ return mService.getApplicationRestrictions(packageName, user.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier());
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ UserHandle user) {
+ try {
+ mService.setApplicationRestrictions(packageName, entries, user.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
+ }
+ }
}
not normally be used by applications; it requires that the system keep
your application running at all times. -->
<attr name="persistent" format="boolean" />
-
+
+ <!-- Flag to specify if this application needs to be present for all users. Only pre-installed
+ applications can request this feature. Default value is false. -->
+ <attr name="requiredForAllUsers" format="boolean" />
+
<!-- Flag indicating whether the application can be debugged, even when
running on a device that is running in user mode. -->
<attr name="debuggable" format="boolean" />
for normal behavior. -->
<attr name="hasCode" format="boolean" />
<attr name="persistent" />
+ <attr name="requiredForAllUsers" />
<!-- Specify whether the components in this application are enabled or not (that is, can be
instantiated by the system).
If "false", it overrides any component specific values (a value of "true" will not
<string name="config_chooseTypeAndAccountActivity"
>android/android.accounts.ChooseTypeAndAccountActivity</string>
+ <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
+ <string name="config_appsAuthorizedForSharedAccounts"></string>
</resources>
<eat-comment />
<public type="attr" name="windowOverscan" />
+ <public type="attr" name="requiredForAllUsers" />
<public type="style" name="Theme.NoTitleBar.Overscan" />
<public type="style" name="Theme.Light.NoTitleBar.Overscan" />
<public type="style" name="Theme.Black.NoTitleBar.Overscan" />
<java-symbol type="string" name="owner_name" />
<java-symbol type="string" name="config_chooseAccountActivity" />
<java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
+ <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
@Override
public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
- Slog.d(TAG, "onServiceChanged() for userId " + userId);
validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
}
if (result != null) {
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
// Create a Session for the target user and pass in the bundle
- Slog.i(TAG, "getAccountCredentialsForCloning returned success, "
- + "sending result to target user");
completeCloningAccount(result, account, toAccounts);
} else {
- Slog.e(TAG, "getAccountCredentialsForCloning returned failure");
clonePassword(fromAccounts, toAccounts, account);
}
return;
} else {
- Slog.e(TAG, "getAccountCredentialsForCloning returned null");
clonePassword(fromAccounts, toAccounts, account);
super.onResult(result);
}
if (result != null) {
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
// TODO: Anything?
- Slog.i(TAG, "addAccount returned success");
} else {
// TODO: Show error notification
// TODO: Should we remove the shadow account to avoid retries?
- Slog.e(TAG, "addAccountFromCredentials returned failure");
}
return;
} else {
- Slog.e(TAG, "addAccountFromCredentials returned null");
super.onResult(result);
}
}
if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
+ // Is user allowed to modify accounts?
+ if (!getUserManager().getUserRestrictions(Binder.getCallingUserHandle())
+ .getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+ "User is not allowed to add an account!");
+ } catch (RemoteException re) {
+ }
+ return;
+ }
+
UserAccounts accounts = getUserAccountsForCaller();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
private volatile Account[] mAccountsOfType = null;
private volatile ArrayList<Account> mAccountsWithFeatures = null;
private volatile int mCurrentAccount = 0;
+ private int mCallingUid;
public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
- IAccountManagerResponse response, String type, String[] features) {
+ IAccountManagerResponse response, String type, String[] features, int callingUid) {
super(accounts, response, type, false /* expectActivityLaunch */,
true /* stripAuthTokenFromResult */);
+ mCallingUid = callingUid;
mFeatures = features;
}
public void run() throws RemoteException {
synchronized (mAccounts.cacheLock) {
- mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType);
+ mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid);
}
// check whether each account matches the requested features
mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
public Account[] getAccounts(int userId) {
checkReadAccountsPermission();
UserAccounts accounts = getUserAccounts(userId);
+ int callingUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
synchronized (accounts.cacheLock) {
- return getAccountsFromCacheLocked(accounts, null);
+ return getAccountsFromCacheLocked(accounts, null, callingUid);
}
} finally {
restoreCallingIdentity(identityToken);
UserAccounts userAccounts = getUserAccounts(userId);
if (userAccounts == null) continue;
synchronized (userAccounts.cacheLock) {
- Account[] accounts = getAccountsFromCacheLocked(userAccounts, null);
+ Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
+ Binder.getCallingUid());
for (int a = 0; a < accounts.length; a++) {
runningAccounts.add(new AccountAndUser(accounts[a], userId));
}
@Override
public Account[] getAccountsAsUser(String type, int userId) {
+ final int callingUid = Binder.getCallingUid();
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
- && Binder.getCallingUid() != android.os.Process.myUid()) {
+ && callingUid != android.os.Process.myUid()) {
throw new SecurityException("User " + UserHandle.getCallingUserId()
+ " trying to get account for " + userId);
}
long identityToken = clearCallingIdentity();
try {
synchronized (accounts.cacheLock) {
- return getAccountsFromCacheLocked(accounts, type);
+ return getAccountsFromCacheLocked(accounts, type, callingUid);
}
} finally {
restoreCallingIdentity(identityToken);
if (type == null) throw new IllegalArgumentException("accountType is null");
checkReadAccountsPermission();
UserAccounts userAccounts = getUserAccountsForCaller();
+ int callingUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
if (features == null || features.length == 0) {
Account[] accounts;
synchronized (userAccounts.cacheLock) {
- accounts = getAccountsFromCacheLocked(userAccounts, type);
+ accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid);
}
Bundle result = new Bundle();
result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
onResult(response, result);
return;
}
- new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features).bind();
+ new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features,
+ callingUid).bind();
} finally {
restoreCallingIdentity(identityToken);
}
}
}
} else {
- Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */);
+ Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
+ android.os.Process.myUid());
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
fout.println(" " + account);
accounts.accountCache.put(account.type, newAccountsForType);
}
- protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType) {
+ private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
+ int callingUid) {
+ if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
+ || callingUid == android.os.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,
+ // otherwise return non-shared accounts only.
+ // This might be a temporary way to specify a whitelist
+ String whiteList = mContext.getResources().getString(
+ com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
+ for (String packageName : packages) {
+ if (whiteList.contains(";" + packageName + ";")) {
+ return unfiltered;
+ }
+ }
+ 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;
+ }
+ }
+ if (!found) {
+ allowed.add(account);
+ }
+ }
+ Account[] filtered = new Account[allowed.size()];
+ allowed.toArray(filtered);
+ return filtered;
+ } else {
+ return unfiltered;
+ }
+ }
+
+ protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
+ int callingUid) {
if (accountType != null) {
final Account[] accounts = userAccounts.accountCache.get(accountType);
if (accounts == null) {
return EMPTY_ACCOUNT_ARRAY;
} else {
- return Arrays.copyOf(accounts, accounts.length);
+ return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
+ callingUid);
}
} else {
int totalLength = 0;
accountsOfType.length);
totalLength += accountsOfType.length;
}
- return accounts;
+ return filterSharedAccounts(userAccounts, accounts, callingUid);
}
}
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;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
private static final String TAG_RESTRICTIONS = "restrictions";
+ private static final String TAG_ENTRY = "entry";
+ private static final String TAG_VALUE = "value";
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_MULTIPLE = "m";
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
private static final String USER_PHOTO_FILENAME = "photo.png";
+ private static final String RESTRICTIONS_FILE_PREFIX = "res_";
+
private static final int MIN_USER_ID = 10;
private static final int USER_VERSION = 2;
}
@Override
+ public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
+ if (UserHandle.getCallingUserId() != userId
+ || Binder.getCallingUid() != getUidForPackage(packageName)) {
+ checkManageUsersPermission("Only system can get restrictions for other users/apps");
+ }
+ synchronized (mPackagesLock) {
+ // Read the restrictions from XML
+ return readApplicationRestrictionsLocked(packageName, userId);
+ }
+ }
+
+ @Override
+ public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ int userId) {
+ if (UserHandle.getCallingUserId() != userId
+ || Binder.getCallingUid() != getUidForPackage(packageName)) {
+ checkManageUsersPermission("Only system can set restrictions for other users/apps");
+ }
+ synchronized (mPackagesLock) {
+ // Write the restrictions to XML
+ writeApplicationRestrictionsLocked(packageName, entries, userId);
+ }
+ }
+
+ private int getUidForPackage(String packageName) {
+ try {
+ return mContext.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES).uid;
+ } catch (NameNotFoundException nnfe) {
+ return -1;
+ }
+ }
+
+ private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName,
+ int userId) {
+ final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
+ final ArrayList<String> values = new ArrayList<String>();
+
+ FileInputStream fis = null;
+ try {
+ AtomicFile restrictionsFile =
+ new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
+ RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
+ fis = restrictionsFile.openRead();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read restrictions file "
+ + restrictionsFile.getBaseFile());
+ return entries;
+ }
+
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
+ String key = parser.getAttributeValue(null, ATTR_KEY);
+ String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
+ if (multiple != null) {
+ int count = Integer.parseInt(multiple);
+ while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG
+ && parser.getName().equals(TAG_VALUE)) {
+ values.add(parser.nextText().trim());
+ count--;
+ }
+ }
+ String [] valueStrings = new String[values.size()];
+ values.toArray(valueStrings);
+ Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings);
+ RestrictionEntry entry = new RestrictionEntry(key, valueStrings);
+ entries.add(entry);
+ } else {
+ String value = parser.nextText().trim();
+ Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value);
+ RestrictionEntry entry = new RestrictionEntry(key, value);
+ entries.add(entry);
+ }
+ }
+ }
+
+ } catch (IOException ioe) {
+ } catch (XmlPullParserException pe) {
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return entries;
+ }
+
+ private void writeApplicationRestrictionsLocked(String packageName,
+ List<RestrictionEntry> entries, int userId) {
+ FileOutputStream fos = null;
+ AtomicFile restrictionsFile = new AtomicFile(
+ new File(Environment.getUserSystemDirectory(userId),
+ RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
+ try {
+ fos = restrictionsFile.startWrite();
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_RESTRICTIONS);
+
+ 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.text(value != null ? value : "");
+ } else {
+ String[] values = entry.getMultipleValues();
+ serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
+ for (String value : values) {
+ serializer.startTag(null, TAG_VALUE);
+ serializer.text(value != null ? value : "");
+ serializer.endTag(null, TAG_VALUE);
+ }
+ }
+ serializer.endTag(null, TAG_ENTRY);
+ }
+
+ serializer.endTag(null, TAG_RESTRICTIONS);
+
+ serializer.endDocument();
+ restrictionsFile.finishWrite(fos);
+ } catch (Exception e) {
+ restrictionsFile.failWrite(fos);
+ Slog.e(LOG_TAG, "Error writing application restrictions list");
+ }
+ }
+
+ @Override
public int getUserSerialNumber(int userHandle) {
synchronized (mPackagesLock) {
if (!exists(userHandle)) return -1;