Merge "Enable use of Surface as a native window in EGL14 wrapper." into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index d246b09..c19acfe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5075,6 +5075,7 @@
public class ContentProviderClient {
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
+ method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
method public android.content.ContentProvider getLocalContentProvider();
method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -5319,9 +5320,10 @@
method public abstract void revokeUriPermission(android.net.Uri, int);
method public abstract void sendBroadcast(android.content.Intent);
method public abstract void sendBroadcast(android.content.Intent, java.lang.String);
- method public void sendBroadcastToUser(android.content.Intent, int);
+ method public abstract void sendBroadcastToUser(android.content.Intent, int);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract void sendOrderedBroadcastToUser(android.content.Intent, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void sendStickyBroadcast(android.content.Intent);
method public abstract void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void setTheme(int);
@@ -5453,8 +5455,10 @@
method public void revokeUriPermission(android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
+ method public void sendBroadcastToUser(android.content.Intent, int);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendOrderedBroadcastToUser(android.content.Intent, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
@@ -5828,7 +5832,7 @@
field public static final java.lang.String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT";
field public static final java.lang.String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY";
field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
- field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
+ field public static final java.lang.String EXTRA_ORIGINATING_URL = "android.intent.extra.ORIGINATING_URL";
field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
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";
@@ -6235,6 +6239,7 @@
field public static final int FLAG_HARDWARE_ACCELERATED = 512; // 0x200
field public static final int FLAG_MULTIPROCESS = 1; // 0x1
field public static final int FLAG_NO_HISTORY = 128; // 0x80
+ field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
field public static final int LAUNCH_MULTIPLE = 0; // 0x0
field public static final int LAUNCH_SINGLE_INSTANCE = 3; // 0x3
@@ -6657,7 +6662,9 @@
ctor public ProviderInfo(android.content.pm.ProviderInfo);
method public int describeContents();
field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public java.lang.String authority;
+ field public int flags;
field public boolean grantUriPermissions;
field public int initOrder;
field public deprecated boolean isSyncable;
@@ -6703,7 +6710,7 @@
method public void dump(android.util.Printer, java.lang.String);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
- field public static final int FLAG_SINGLE_USER = 4; // 0x4
+ field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
field public int flags;
field public java.lang.String permission;
@@ -11505,6 +11512,7 @@
method public void setVideoSource(int) throws java.lang.IllegalStateException;
method public void start() throws java.lang.IllegalStateException;
method public void stop() throws java.lang.IllegalStateException;
+ field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321
@@ -16248,6 +16256,7 @@
method public static final int myPid();
method public static final int myTid();
method public static final int myUid();
+ method public static final int myUserHandle();
method public static final void sendSignal(int, int);
method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
@@ -21143,8 +21152,10 @@
method public void revokeUriPermission(android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
+ method public void sendBroadcastToUser(android.content.Intent, int);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendOrderedBroadcastToUser(android.content.Intent, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 4311c06..4cb5270 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -782,7 +782,6 @@
String macAlgo = null;
byte[] macKey = null;
byte[] tag = null;
- String referrer = null;
while ((opt=nextOption()) != null) {
if (opt.equals("-l")) {
@@ -846,13 +845,6 @@
showUsage();
return;
}
- } else if (opt.equals("--referrer")) {
- referrer = nextOptionData();
- if (referrer == null) {
- System.err.println("Error: must supply argument for --referrer");
- showUsage();
- return;
- }
} else {
System.err.println("Error: Unknown option: " + opt);
showUsage();
@@ -900,13 +892,6 @@
final Uri apkURI;
final Uri verificationURI;
- final Uri referrerURI;
-
- if (referrer != null) {
- referrerURI = Uri.parse(referrer);
- } else {
- referrerURI = null;
- }
// Populate apkURI, must be present
final String apkFilePath = nextArg();
@@ -931,7 +916,7 @@
PackageInstallObserver obs = new PackageInstallObserver();
try {
mPm.installPackageWithVerification(apkURI, obs, installFlags, installerPackageName,
- verificationURI, null, encryptionParams, apkURI, referrerURI);
+ verificationURI, null, encryptionParams);
synchronized (obs) {
while (!obs.finished) {
@@ -1451,8 +1436,7 @@
System.err.println(" pm list libraries");
System.err.println(" pm path PACKAGE");
System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
- System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]");
- System.err.println(" [--referrer <URI>] PATH");
+ System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>] PATH");
System.err.println(" pm uninstall [-k] PACKAGE");
System.err.println(" pm clear PACKAGE");
System.err.println(" pm enable PACKAGE_OR_COMPONENT");
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 92b6f72..c74f823 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -529,7 +529,36 @@
throws SecurityException {
try {
return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
- flags);
+ flags, UserId.myUserId());
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return null;
+ }
+ }
+
+ /**
+ * Same as {@link #getRecentTasks(int, int)} but returns the recent tasks for a
+ * specific user. It requires holding
+ * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+ * @param maxNum The maximum number of entries to return in the list. The
+ * actual number returned may be smaller, depending on how many tasks the
+ * user has started and the maximum number the system can remember.
+ * @param flags Information about what to return. May be any combination
+ * of {@link #RECENT_WITH_EXCLUDED} and {@link #RECENT_IGNORE_UNAVAILABLE}.
+ *
+ * @return Returns a list of RecentTaskInfo records describing each of
+ * the recent tasks.
+ *
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not hold the {@link android.Manifest.permission#GET_TASKS} or the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permissions.
+ * @hide
+ */
+ public List<RecentTaskInfo> getRecentTasksForUser(int maxNum, int flags, int userId)
+ throws SecurityException {
+ try {
+ return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
+ flags, userId);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
return null;
@@ -1840,6 +1869,18 @@
return PackageManager.PERMISSION_DENIED;
}
+ /** @hide */
+ public static int checkUidPermission(String permission, int uid) {
+ try {
+ return AppGlobals.getPackageManager()
+ .checkUidPermission(permission, uid);
+ } catch (RemoteException e) {
+ // Should never happen, but if it does... deny!
+ Slog.e(TAG, "PackageManager is dead?!?", e);
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+
/**
* Returns the usage statistics of each installed package.
*
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e12fa19..88e7344 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -39,6 +39,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
+import android.os.UserId;
import android.text.TextUtils;
import android.util.Log;
import android.util.Singleton;
@@ -135,6 +136,31 @@
return true;
}
+ case START_ACTIVITY_AS_USER_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ IBinder resultTo = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ int startFlags = data.readInt();
+ String profileFile = data.readString();
+ ParcelFileDescriptor profileFd = data.readInt() != 0
+ ? data.readFileDescriptor() : null;
+ Bundle options = data.readInt() != 0
+ ? Bundle.CREATOR.createFromParcel(data) : null;
+ int userId = data.readInt();
+ int result = startActivityAsUser(app, intent, resolvedType,
+ resultTo, resultWho, requestCode, startFlags,
+ profileFile, profileFd, options, userId);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
case START_ACTIVITY_AND_WAIT_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -454,8 +480,9 @@
data.enforceInterface(IActivityManager.descriptor);
int maxNum = data.readInt();
int fl = data.readInt();
+ int userId = data.readInt();
List<ActivityManager.RecentTaskInfo> list = getRecentTasks(maxNum,
- fl);
+ fl, userId);
reply.writeNoException();
reply.writeTypedList(list);
return true;
@@ -1764,6 +1791,42 @@
data.recycle();
return result;
}
+
+ public int startActivityAsUser(IApplicationThread caller, Intent intent,
+ String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+ int startFlags, String profileFile,
+ ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ intent.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ data.writeStrongBinder(resultTo);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ data.writeInt(startFlags);
+ data.writeString(profileFile);
+ if (profileFd != null) {
+ data.writeInt(1);
+ profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ data.writeInt(0);
+ }
+ if (options != null) {
+ data.writeInt(1);
+ options.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ data.writeInt(userId);
+ mRemote.transact(START_ACTIVITY_AS_USER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
public WaitResult startActivityAndWait(IApplicationThread caller, Intent intent,
String resolvedType, IBinder resultTo, String resultWho,
int requestCode, int startFlags, String profileFile,
@@ -2163,12 +2226,13 @@
return list;
}
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
- int flags) throws RemoteException {
+ int flags, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(maxNum);
data.writeInt(flags);
+ data.writeInt(userId);
mRemote.transact(GET_RECENT_TASKS_TRANSACTION, data, reply, 0);
reply.readException();
ArrayList<ActivityManager.RecentTaskInfo> list
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9aa2505..9a50a41 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -972,24 +972,12 @@
}
@Override
- public void installPackageWithOrigin(Uri packageURI, IPackageInstallObserver observer,
- int flags, String installerPackageName, Uri originatingURI, Uri referrer) {
- try {
- mPM.installPackageWithOrigin(packageURI, observer, flags, null, originatingURI,
- referrer);
- } catch (RemoteException e) {
- // Should never happen!
- }
- }
-
- @Override
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
- ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams,
- Uri originatingURI, Uri referrer) {
+ ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
try {
mPM.installPackageWithVerification(packageURI, observer, flags, installerPackageName,
- verificationURI, manifestDigest, encryptionParams, originatingURI, referrer);
+ verificationURI, manifestDigest, encryptionParams);
} catch (RemoteException e) {
// Should never happen!
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3c8a290..d886278 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -893,6 +893,18 @@
startActivity(intent, null);
}
+ /** @hide */
+ @Override
+ public void startActivityAsUser(Intent intent, int userId) {
+ try {
+ ActivityManagerNative.getDefault().startActivityAsUser(
+ mMainThread.getApplicationThread(), intent,
+ intent.resolveTypeIfNeeded(getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, null, userId);
+ } catch (RemoteException re) {
+ }
+ }
+
@Override
public void startActivity(Intent intent, Bundle options) {
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
@@ -967,18 +979,6 @@
}
@Override
- public void sendBroadcastToUser(Intent intent, int userId) {
- String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
- try {
- intent.setAllowFds(false);
- ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
- intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false,
- userId);
- } catch (RemoteException e) {
- }
- }
-
- @Override
public void sendBroadcast(Intent intent, String receiverPermission) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
@@ -1039,6 +1039,50 @@
}
@Override
+ public void sendBroadcastToUser(Intent intent, int userHandle) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.setAllowFds(false);
+ ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
+ intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false,
+ userHandle);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void sendOrderedBroadcastToUser(Intent intent, int userHandle,
+ BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ IIntentReceiver rd = null;
+ if (resultReceiver != null) {
+ if (mPackageInfo != null) {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = mPackageInfo.getReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler,
+ mMainThread.getInstrumentation(), false);
+ } else {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = new LoadedApk.ReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+ }
+ }
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.setAllowFds(false);
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, rd,
+ initialCode, initialData, initialExtras, null,
+ true, false, userHandle);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void sendStickyBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
@@ -1197,7 +1241,7 @@
/** @hide */
@Override
- public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+ public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
@@ -1219,7 +1263,7 @@
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
- sd, flags, userId);
+ sd, flags, userHandle);
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 17700f9..0b1c524 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -344,6 +344,13 @@
*/
public static final int NETWORK_WIFI = 1 << 1;
+ /**
+ * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
+ * {@link ConnectivityManager#TYPE_BLUETOOTH}.
+ * @hide
+ */
+ public static final int NETWORK_BLUETOOTH = 1 << 2;
+
private Uri mUri;
private Uri mDestinationUri;
private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 031e39b..a6d1995 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -55,6 +55,10 @@
Intent intent, String resolvedType, IBinder resultTo, String resultWho,
int requestCode, int flags, String profileFile,
ParcelFileDescriptor profileFd, Bundle options) throws RemoteException;
+ public int startActivityAsUser(IApplicationThread caller,
+ Intent intent, String resolvedType, IBinder resultTo, String resultWho,
+ int requestCode, int flags, String profileFile,
+ ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException;
public WaitResult startActivityAndWait(IApplicationThread caller,
Intent intent, String resolvedType, IBinder resultTo, String resultWho,
int requestCode, int flags, String profileFile,
@@ -102,7 +106,7 @@
public List getTasks(int maxNum, int flags,
IThumbnailReceiver receiver) throws RemoteException;
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
- int flags) throws RemoteException;
+ int flags, int userId) throws RemoteException;
public ActivityManager.TaskThumbnails getTaskThumbnails(int taskId) throws RemoteException;
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
@@ -606,4 +610,5 @@
int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149;
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
int IS_INTENT_SENDER_AN_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+151;
+ int START_ACTIVITY_AS_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+152;
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 5c315ce..204f963 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -231,6 +231,19 @@
}
}
+ /** See {@link ContentProvider#call(String, String, Bundle)} */
+ public Bundle call(String method, String arg, Bundle extras)
+ throws RemoteException {
+ try {
+ return mContentProvider.call(method, arg, extras);
+ } catch (DeadObjectException e) {
+ if (!mStable) {
+ mContentResolver.unstableProviderDied(mContentProvider);
+ }
+ throw e;
+ }
+ }
+
/**
* Call this to indicate to the system that the associated {@link ContentProvider} is no
* longer needed by this {@link ContentProviderClient}.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index af8b213..5a7a989 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -851,6 +851,18 @@
public abstract void startActivity(Intent intent);
/**
+ * Same as {@link #startActivity(Intent)}, but for a specific user. It requires holding
+ * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+ * @param intent The description of the activity to start.
+ * @param userId The user id of the user to start this activity for.
+ * @throws ActivityNotFoundException
+ * @hide
+ */
+ public void startActivityAsUser(Intent intent, int userId) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Launch a new activity. You will not receive any information about when
* the activity exits.
*
@@ -988,18 +1000,6 @@
public abstract void sendBroadcast(Intent intent);
/**
- * Same as #sendBroadcast(Intent intent), but for a specific user. This broadcast
- * can only be sent to receivers that are part of the calling application. It
- * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
- * permission.
- * @param intent the intent to broadcast
- * @param userId user to send the intent to
- */
- public void sendBroadcastToUser(Intent intent, int userId) {
- throw new RuntimeException("Not implemented. Must override in a subclass.");
- }
-
- /**
* Broadcast the given intent to all interested BroadcastReceivers, allowing
* an optional required permission to be enforced. This
* call is asynchronous; it returns immediately, and you will continue
@@ -1097,6 +1097,48 @@
Bundle initialExtras);
/**
+ * Same as {@link #sendBroadcast(Intent)}, but for a specific user. This broadcast
+ * can only be sent to receivers that are part of the calling application. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * permission.
+ * @param intent The intent to broadcast
+ * @param userHandle User to send the intent to.
+ * @see #sendBroadcast(Intent)
+ */
+ public abstract void sendBroadcastToUser(Intent intent, int userHandle);
+
+ /**
+ * Same as
+ * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)},
+ * but for a specific user. This broadcast
+ * can only be sent to receivers that are part of the calling application. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * permission.
+ *
+ * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param userHandle User to send the intent to.
+ * @param resultReceiver Your own BroadcastReceiver to treat as the final
+ * receiver of the broadcast.
+ * @param scheduler A custom Handler with which to schedule the
+ * resultReceiver callback; if null it will be
+ * scheduled in the Context's main thread.
+ * @param initialCode An initial value for the result code. Often
+ * Activity.RESULT_OK.
+ * @param initialData An initial value for the result data. Often
+ * null.
+ * @param initialExtras An initial value for the result extras. Often
+ * null.
+ *
+ * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+ */
+ public abstract void sendOrderedBroadcastToUser(Intent intent, int userHandle,
+ BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras);
+
+ /**
* Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
* Intent you are sending stays around after the broadcast is complete,
* so that others can quickly retrieve that data through the return
@@ -1403,11 +1445,11 @@
int flags);
/**
- * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userId
+ * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userHandle
* argument for use by system server and other multi-user aware code.
* @hide
*/
- public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+ public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 7738132..f007720 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -276,6 +276,12 @@
mBase.startActivity(intent);
}
+ /** @hide */
+ @Override
+ public void startActivityAsUser(Intent intent, int userId) {
+ mBase.startActivityAsUser(intent, userId);
+ }
+
@Override
public void startActivity(Intent intent, Bundle options) {
mBase.startActivity(intent, options);
@@ -313,11 +319,6 @@
}
@Override
- public void sendBroadcastToUser(Intent intent, int userId) {
- mBase.sendBroadcastToUser(intent, userId);
- }
-
- @Override
public void sendBroadcast(Intent intent, String receiverPermission) {
mBase.sendBroadcast(intent, receiverPermission);
}
@@ -339,6 +340,19 @@
}
@Override
+ public void sendBroadcastToUser(Intent intent, int userHandle) {
+ mBase.sendBroadcastToUser(intent, userHandle);
+ }
+
+ @Override
+ public void sendOrderedBroadcastToUser(Intent intent, int userHandle,
+ BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ mBase.sendOrderedBroadcastToUser(intent, userHandle, resultReceiver,
+ scheduler, initialCode, initialData, initialExtras);
+ }
+
+ @Override
public void sendStickyBroadcast(Intent intent) {
mBase.sendStickyBroadcast(intent);
}
@@ -395,8 +409,8 @@
/** @hide */
@Override
- public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
- return mBase.bindService(service, conn, flags, userId);
+ public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
+ return mBase.bindService(service, conn, flags, userHandle);
}
@Override
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6d2de8e..d325186 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -571,7 +571,7 @@
* <li> {@link #EXTRA_INITIAL_INTENTS}
* <li> {@link #EXTRA_INTENT}
* <li> {@link #EXTRA_KEY_EVENT}
- * <li> {@link #EXTRA_ORIGINATING_URI}
+ * <li> {@link #EXTRA_ORIGINATING_URL}
* <li> {@link #EXTRA_PHONE_NUMBER}
* <li> {@link #EXTRA_REFERRER}
* <li> {@link #EXTRA_REMOTE_INTENT_TOKEN}
@@ -1288,17 +1288,17 @@
= "android.intent.extra.NOT_UNKNOWN_SOURCE";
/**
- * Used as a URI extra field with {@link #ACTION_INSTALL_PACKAGE} and
- * {@link #ACTION_VIEW} to indicate the URI from which the local APK in the Intent
+ * Used as a string extra field with {@link #ACTION_INSTALL_PACKAGE} and
+ * {@link #ACTION_VIEW} to indicate the URL from which the local APK in the Intent
* data field originated from.
*/
- public static final String EXTRA_ORIGINATING_URI
- = "android.intent.extra.ORIGINATING_URI";
+ public static final String EXTRA_ORIGINATING_URL
+ = "android.intent.extra.ORIGINATING_URL";
/**
- * Used as a URI extra field with {@link #ACTION_INSTALL_PACKAGE} and
- * {@link #ACTION_VIEW} to indicate the HTTP referrer URI associated with the Intent
- * data field or {@link #EXTRA_ORIGINATING_URI}.
+ * Used as a string extra field with {@link #ACTION_INSTALL_PACKAGE} and
+ * {@link #ACTION_VIEW} to indicate the HTTP referrer associated with the Intent
+ * data field or {@link #EXTRA_ORIGINATING_URL}.
*/
public static final String EXTRA_REFERRER
= "android.intent.extra.REFERRER";
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 35e5610..3035729 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -172,6 +172,14 @@
*/
public static final int FLAG_IMMERSIVE = 0x0400;
/**
+ * Bit in {@link #flags}: If set, a single instance of the receiver will
+ * run for all users on the device. Set from the
+ * {@link android.R.attr#singleUser} attribute. Note that this flag is
+ * only relevent for ActivityInfo structures that are describiner receiver
+ * components; it is not applied to activities.
+ */
+ public static final int FLAG_SINGLE_USER = 0x40000000;
+ /**
* Options that have been set in the activity declaration in the
* manifest.
* These include:
@@ -181,7 +189,7 @@
* {@link #FLAG_STATE_NOT_NEEDED}, {@link #FLAG_EXCLUDE_FROM_RECENTS},
* {@link #FLAG_ALLOW_TASK_REPARENTING}, {@link #FLAG_NO_HISTORY},
* {@link #FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS},
- * {@link #FLAG_HARDWARE_ACCELERATED}
+ * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER}.
*/
public int flags;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1b28a33..0d87df5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -352,7 +352,7 @@
String nextPackageToClean(String lastPackage);
void movePackage(String packageName, IPackageMoveObserver observer, int flags);
-
+
boolean addPermissionAsync(in PermissionInfo info);
boolean setInstallLocation(int loc);
@@ -363,14 +363,9 @@
void setUserName(int userId, String name);
ParcelFileDescriptor setUserIcon(int userId);
- void installPackageWithOrigin(in Uri packageURI, in IPackageInstallObserver observer,
- int flags, in String installerPackageName, in Uri originatingURI,
- in Uri referrer);
-
void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
int flags, in String installerPackageName, in Uri verificationURI,
- in ManifestDigest manifestDigest, in ContainerEncryptionParams encryptionParams,
- in Uri originatingURI, in Uri referrer);
+ in ManifestDigest manifestDigest, in ContainerEncryptionParams encryptionParams);
void verifyPendingInstall(int id, int verificationCode);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f745c89..cd7ef0e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2196,33 +2196,6 @@
/**
* Similar to
* {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
- * with references to the location of where the package originated from.
- *
- * @param packageURI The location of the package file to install. This can
- * be a 'file:' or a 'content:' URI.
- * @param observer An observer callback to get notified when the package
- * installation is complete.
- * {@link IPackageInstallObserver#packageInstalled(String, int)}
- * will be called when that happens. observer may be null to
- * indicate that no callback is desired.
- * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
- * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}
- * @param installerPackageName Optional package name of the application that
- * is performing the installation. This identifies which market
- * the package came from.
- * @param originatingURI URI referencing where the package was downloaded
- * from. May be {@code null}.
- * @param referrer HTTP referrer URI associated with the originatingURI.
- * May be {@code null}.
- * @hide
- */
- public abstract void installPackageWithOrigin(
- Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName, Uri originatingURI, Uri referrer);
-
- /**
- * Similar to
- * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
* with an extra verification file provided.
*
* @param packageURI The location of the package file to install. This can
@@ -2246,17 +2219,12 @@
* @param encryptionParams if the package to be installed is encrypted,
* these parameters describing the encryption and authentication
* used. May be {@code null}.
- * @param originatingURI URI referencing where the package was downloaded
- * from. May be {@code null}.
- * @param referrer HTTP referrer URI associated with the originatingURI.
- * May be {@code null}.
* @hide
*/
public abstract void installPackageWithVerification(Uri packageURI,
IPackageInstallObserver observer, int flags, String installerPackageName,
Uri verificationURI, ManifestDigest manifestDigest,
- ContainerEncryptionParams encryptionParams, Uri originatingURI,
- Uri referrer);
+ ContainerEncryptionParams encryptionParams);
/**
* Allows a package listening to the
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3ce7c78..d906401 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2040,7 +2040,7 @@
return null;
}
- final boolean setExported = sa.hasValue(
+ boolean setExported = sa.hasValue(
com.android.internal.R.styleable.AndroidManifestActivity_exported);
if (setExported) {
a.info.exported = sa.getBoolean(
@@ -2166,6 +2166,21 @@
a.info.configChanges = 0;
}
+ if (receiver) {
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestActivity_singleUser,
+ false)) {
+ a.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
+ if (a.info.exported) {
+ Slog.w(TAG, "Activity exported request ignored due to singleUser: "
+ + a.className + " at " + mArchiveSourcePath + " "
+ + parser.getPositionDescription());
+ a.info.exported = false;
+ }
+ setExported = true;
+ }
+ }
+
sa.recycle();
if (receiver && (owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
@@ -2428,8 +2443,18 @@
return null;
}
+ boolean providerExportedDefault = false;
+
+ if (owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ // For compatibility, applications targeting API level 16 or lower
+ // should have their content providers exported by default, unless they
+ // specify otherwise.
+ providerExportedDefault = true;
+ }
+
p.info.exported = sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestProvider_exported, true);
+ com.android.internal.R.styleable.AndroidManifestProvider_exported,
+ providerExportedDefault);
String cpname = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestProvider_authorities, 0);
@@ -2475,6 +2500,20 @@
com.android.internal.R.styleable.AndroidManifestProvider_initOrder,
0);
+ p.info.flags = 0;
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestProvider_singleUser,
+ false)) {
+ p.info.flags |= ProviderInfo.FLAG_SINGLE_USER;
+ if (p.info.exported) {
+ Slog.w(TAG, "Provider exported request ignored due to singleUser: "
+ + p.className + " at " + mArchiveSourcePath + " "
+ + parser.getPositionDescription());
+ p.info.exported = false;
+ }
+ }
+
sa.recycle();
if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
@@ -2487,7 +2526,7 @@
}
if (cpname == null) {
- outError[0] = "<provider> does not incude authorities attribute";
+ outError[0] = "<provider> does not include authorities attribute";
return null;
}
p.info.authority = cpname.intern();
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index ec01775..a534176 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -73,7 +73,21 @@
/** Used to control initialization order of single-process providers
* running in the same process. Higher goes first. */
public int initOrder = 0;
-
+
+ /**
+ * Bit in {@link #flags}: If set, a single instance of the provider will
+ * run for all users on the device. Set from the
+ * {@link android.R.attr#singleUser} attribute.
+ */
+ public static final int FLAG_SINGLE_USER = 0x40000000;
+
+ /**
+ * Options that have been set in the provider declaration in the
+ * manifest.
+ * These include: {@link #FLAG_SINGLE_USER}.
+ */
+ public int flags = 0;
+
/**
* Whether or not this provider is syncable.
* @deprecated This flag is now being ignored. The current way to make a provider
@@ -95,6 +109,7 @@
pathPermissions = orig.pathPermissions;
multiprocess = orig.multiprocess;
initOrder = orig.initOrder;
+ flags = orig.flags;
isSyncable = orig.isSyncable;
}
@@ -112,6 +127,7 @@
out.writeTypedArray(pathPermissions, parcelableFlags);
out.writeInt(multiprocess ? 1 : 0);
out.writeInt(initOrder);
+ out.writeInt(flags);
out.writeInt(isSyncable ? 1 : 0);
}
@@ -127,8 +143,7 @@
};
public String toString() {
- return "ContentProviderInfo{name=" + authority + " className=" + name
- + " isSyncable=" + (isSyncable ? "true" : "false") + "}";
+ return "ContentProviderInfo{name=" + authority + " className=" + name + "}";
}
private ProviderInfo(Parcel in) {
@@ -141,6 +156,7 @@
pathPermissions = in.createTypedArray(PathPermission.CREATOR);
multiprocess = in.readInt() != 0;
initOrder = in.readInt();
+ flags = in.readInt();
isSyncable = in.readInt() != 0;
}
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 1aaceb4..796c2a4 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -53,13 +53,14 @@
* run for all users on the device. Set from the
* {@link android.R.attr#singleUser} attribute.
*/
- public static final int FLAG_SINGLE_USER = 0x0004;
+ public static final int FLAG_SINGLE_USER = 0x40000000;
/**
* Options that have been set in the service declaration in the
* manifest.
* These include:
- * {@link #FLAG_STOP_WITH_TASK}, {@link #FLAG_ISOLATED_PROCESS}.
+ * {@link #FLAG_STOP_WITH_TASK}, {@link #FLAG_ISOLATED_PROCESS},
+ * {@link #FLAG_SINGLE_USER}.
*/
public int flags;
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index fb04817..e7ff92d 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -424,6 +424,9 @@
if (mSelfObserver != null && mSelfObserverRegistered == true) {
mContentResolver.unregisterContentObserver(mSelfObserver);
}
+ try {
+ if (!mClosed) close();
+ } catch(Exception e) { }
}
/**
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 7cf869c..8860e0a 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -414,6 +414,15 @@
/**
* Moar jelly beans!
+ *
+ * <p>Applications targeting this or a later release will get these
+ * new changes in behavior:</p>
+ * <ul>
+ * <li>Content Providers: The default value of {@code android:exported} is now
+ * {@code false}. See
+ * <a href="{@docRoot}guide/topics/manifest/provider-element.html#exported">
+ * the android:exported section</a> in the provider documentation for more details.</li>
+ * </ul>
*/
public static final int JELLY_BEAN_MR1 = 17;
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index a04ad93..93860aa 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -640,11 +640,24 @@
public static final native int myTid();
/**
- * Returns the identifier of this process's user.
+ * Returns the identifier of this process's uid. This is the kernel uid
+ * that the process is running under, which is the identity of its
+ * app-specific sandbox. It is different from {@link #myUserHandle} in that
+ * a uid identifies a specific app sandbox in a specific user.
*/
public static final native int myUid();
/**
+ * Returns the identifier of this process's user handle. This is the
+ * user the process is running under. It is distinct from
+ * {@link #myUid()} in that a particular user will have multiple
+ * distinct apps running under it each with their own uid.
+ */
+ public static final int myUserHandle() {
+ return UserId.getUserId(myUid());
+ }
+
+ /**
* Returns whether the current process is in an isolated sandbox.
* @hide
*/
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index ce213fb..f682abe 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -407,17 +407,17 @@
}
/**
- * Enable detection of disk reads.
+ * Enable detection of slow calls.
*/
public Builder detectCustomSlowCalls() {
return enable(DETECT_CUSTOM);
}
/**
- * Enable detection of disk reads.
+ * Disable detection of slow calls.
*/
public Builder permitCustomSlowCalls() {
- return enable(DETECT_CUSTOM);
+ return disable(DETECT_CUSTOM);
}
/**
diff --git a/core/java/android/os/SystemService.java b/core/java/android/os/SystemService.java
index da27db5..db58012 100644
--- a/core/java/android/os/SystemService.java
+++ b/core/java/android/os/SystemService.java
@@ -16,15 +16,53 @@
package android.os;
-/** @hide */
-public class SystemService
-{
- /** Request that the init daemon start a named service. */
+import com.google.android.collect.Maps;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Controls and utilities for low-level {@code init} services.
+ *
+ * @hide
+ */
+public class SystemService {
+
+ private static HashMap<String, State> sStates = Maps.newHashMap();
+
+ /**
+ * State of a known {@code init} service.
+ */
+ public enum State {
+ RUNNING("running"),
+ STOPPING("stopping"),
+ STOPPED("stopped"),
+ RESTARTING("restarting");
+
+ State(String state) {
+ sStates.put(state, this);
+ }
+ }
+
+ private static Object sPropertyLock = new Object();
+
+ static {
+ SystemProperties.addChangeCallback(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (sPropertyLock) {
+ sPropertyLock.notifyAll();
+ }
+ }
+ });
+ }
+
+ /** Request that the init daemon start a named service. */
public static void start(String name) {
SystemProperties.set("ctl.start", name);
}
-
- /** Request that the init daemon stop a named service. */
+
+ /** Request that the init daemon stop a named service. */
public static void stop(String name) {
SystemProperties.set("ctl.stop", name);
}
@@ -33,4 +71,77 @@
public static void restart(String name) {
SystemProperties.set("ctl.restart", name);
}
+
+ /**
+ * Return current state of given service.
+ */
+ public static State getState(String service) {
+ final String rawState = SystemProperties.get("init.svc." + service);
+ final State state = sStates.get(rawState);
+ if (state != null) {
+ return state;
+ } else {
+ throw new IllegalStateException("Service " + service + " in unknown state " + rawState);
+ }
+ }
+
+ /**
+ * Check if given service is {@link State#STOPPED}.
+ */
+ public static boolean isStopped(String service) {
+ return State.STOPPED.equals(getState(service));
+ }
+
+ /**
+ * Check if given service is {@link State#RUNNING}.
+ */
+ public static boolean isRunning(String service) {
+ return State.RUNNING.equals(getState(service));
+ }
+
+ /**
+ * Wait until given service has entered specific state.
+ */
+ public static void waitForState(String service, State state, long timeoutMillis)
+ throws TimeoutException {
+ final long endMillis = SystemClock.elapsedRealtime() + timeoutMillis;
+ while (true) {
+ synchronized (sPropertyLock) {
+ final State currentState = getState(service);
+ if (state.equals(currentState)) {
+ return;
+ }
+
+ if (SystemClock.elapsedRealtime() >= endMillis) {
+ throw new TimeoutException("Service " + service + " currently " + currentState
+ + "; waited " + timeoutMillis + "ms for " + state);
+ }
+
+ try {
+ sPropertyLock.wait(timeoutMillis);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Wait until any of given services enters {@link State#STOPPED}.
+ */
+ public static void waitForAnyStopped(String... services) {
+ while (true) {
+ synchronized (sPropertyLock) {
+ for (String service : services) {
+ if (State.STOPPED.equals(getState(service))) {
+ return;
+ }
+ }
+
+ try {
+ sPropertyLock.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
index 8bf6c6e..7e611df 100644
--- a/core/java/android/os/UserId.java
+++ b/core/java/android/os/UserId.java
@@ -27,8 +27,13 @@
*/
public static final int PER_USER_RANGE = 100000;
+ /** A user id to indicate all users on the device */
public static final int USER_ALL = -1;
+ /** A user id to indicate the currently active user */
+ public static final int USER_CURRENT = -2;
+
+
/**
* Enable multi-user related side effects. Set this to false if there are problems with single
* user usecases.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8e123ac..0c16565 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -345,10 +345,10 @@
* directory provider URIs by themselves. This level of indirection allows
* Contacts Provider to implement additional system-level features and
* optimizations. Access to Contacts Provider is protected by the
- * READ_CONTACTS permission, but access to the directory provider is not.
- * Therefore directory providers must reject requests coming from clients
- * other than the Contacts Provider itself. An easy way to prevent such
- * unauthorized access is to check the name of the calling package:
+ * READ_CONTACTS permission, but access to the directory provider is protected by
+ * BIND_DIRECTORY_SEARCH. This permission was introduced at the API level 17, for previous
+ * platform versions the provider should perform the following check to make sure the call
+ * is coming from the ContactsProvider:
* <pre>
* private boolean isCallerAllowed() {
* PackageManager pm = getContext().getPackageManager();
diff --git a/core/java/android/service/dreams/DreamManagerService.java b/core/java/android/service/dreams/DreamManagerService.java
index d6b38a1..dd177cb 100644
--- a/core/java/android/service/dreams/DreamManagerService.java
+++ b/core/java/android/service/dreams/DreamManagerService.java
@@ -5,8 +5,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import com.android.internal.view.IInputMethod;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -16,34 +14,32 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.provider.Settings;
-import android.util.Log;
import android.util.Slog;
import android.view.IWindowManager;
import android.view.WindowManager;
/**
- *
+ *
* @hide
*
*/
-public class DreamManagerService
- extends IDreamManager.Stub
+public class DreamManagerService
+ extends IDreamManager.Stub
implements ServiceConnection
{
private static final boolean DEBUG = true;
private static final String TAG = "DreamManagerService";
-
+
final Object mLock = new Object[0];
private Context mContext;
private IWindowManager mIWindowManager;
-
+
private ComponentName mCurrentDreamComponent;
private IDreamService mCurrentDream;
- private Binder mCurrentDreamToken;
+ private Binder mCurrentDreamToken;
public DreamManagerService(Context context) {
if (DEBUG) Slog.v(TAG, "DreamManagerService startup");
@@ -60,6 +56,7 @@
}
// IDreamManager method
+ @Override
public void dream() {
ComponentName name = getDreamComponent();
if (name != null) {
@@ -75,26 +72,26 @@
}
// IDreamManager method
+ @Override
public void setDreamComponent(ComponentName name) {
Settings.Secure.putString(mContext.getContentResolver(), SCREENSAVER_COMPONENT, name.flattenToString());
}
-
+
// IDreamManager method
+ @Override
public ComponentName getDreamComponent() {
- // TODO(dsandler) don't load this every time, watch the value
+ // TODO(dsandler) don't load this every time, watch the value
String component = Settings.Secure.getString(mContext.getContentResolver(), SCREENSAVER_COMPONENT);
- if (component == null) {
- component = mContext.getResources().getString(
- com.android.internal.R.string.config_defaultDreamComponent);
- }
if (component != null) {
return ComponentName.unflattenFromString(component);
} else {
+ // We rely on DatabaseHelper to set a sane default for us when the settings DB is upgraded
return null;
}
}
-
+
// IDreamManager method
+ @Override
public void testDream(ComponentName name) {
if (DEBUG) Slog.v(TAG, "startDream name=" + name
+ " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -110,6 +107,7 @@
}
// IDreamManager method
+ @Override
public void awaken() {
if (DEBUG) Slog.v(TAG, "awaken()");
synchronized (mLock) {
@@ -123,6 +121,7 @@
}
// IDreamManager method
+ @Override
public boolean isDreaming() {
return mCurrentDream != null;
}
@@ -141,14 +140,14 @@
mCurrentDreamComponent = componentName;
mCurrentDreamToken = new Binder();
try {
- if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken
+ if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken
+ " for window type: " + WindowManager.LayoutParams.TYPE_DREAM);
mIWindowManager.addWindowToken(mCurrentDreamToken,
WindowManager.LayoutParams.TYPE_DREAM);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to add window token. Proceed at your own risk.");
}
-
+
if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
Slog.w(TAG, "unable to bind service: " + componentName);
}
@@ -172,7 +171,7 @@
if (DEBUG) Slog.v(TAG, "disconnected: " + name + " service: " + mCurrentDream);
// Only happens in exceptional circumstances
}
-
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index b5cfc11..a77425a 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -82,11 +82,17 @@
}
@Override
- void end() {
+ void end(Canvas currentCanvas) {
+ if (currentCanvas instanceof GLES20Canvas) {
+ ((GLES20Canvas) currentCanvas).resume();
+ }
}
@Override
- HardwareCanvas start() {
+ HardwareCanvas start(Canvas currentCanvas) {
+ if (currentCanvas instanceof GLES20Canvas) {
+ ((GLES20Canvas) currentCanvas).interrupt();
+ }
return getCanvas();
}
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index 6ad36ef..e198ef6 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -56,12 +57,12 @@
}
@Override
- HardwareCanvas start() {
+ HardwareCanvas start(Canvas currentCanvas) {
return null;
}
@Override
- void end() {
+ void end(Canvas currentCanvas) {
}
SurfaceTexture getSurfaceTexture() {
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 8fdf104..06c6e7c 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -17,6 +17,7 @@
package android.view;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -143,13 +144,15 @@
/**
* This must be invoked before drawing onto this layer.
+ * @param currentCanvas
*/
- abstract HardwareCanvas start();
+ abstract HardwareCanvas start(Canvas currentCanvas);
/**
* This must be invoked after drawing onto this layer.
+ * @param currentCanvas
*/
- abstract void end();
+ abstract void end(Canvas currentCanvas);
/**
* Copies this layer into the specified bitmap.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d3457b3..f4d40cb 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -531,7 +531,7 @@
mSurface.transferFrom(mNewSurface);
- if (visible) {
+ if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3993432..afcbaaf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1408,6 +1408,7 @@
disposeResizeBuffer();
boolean completed = false;
+ HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas();
HardwareCanvas layerCanvas = null;
try {
if (mResizeBuffer == null) {
@@ -1417,7 +1418,7 @@
mResizeBuffer.getHeight() != mHeight) {
mResizeBuffer.resize(mWidth, mHeight);
}
- layerCanvas = mResizeBuffer.start();
+ layerCanvas = mResizeBuffer.start(hwRendererCanvas);
layerCanvas.setViewport(mWidth, mHeight);
layerCanvas.onPreDraw(null);
final int restoreCount = layerCanvas.save();
@@ -1456,7 +1457,7 @@
layerCanvas.onPostDraw();
}
if (mResizeBuffer != null) {
- mResizeBuffer.end();
+ mResizeBuffer.end(hwRendererCanvas);
if (!completed) {
mResizeBuffer.destroy();
mResizeBuffer = null;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index a28e9ff..860e583 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -985,9 +985,13 @@
}
private void updateDrawableBounds(int w, int h) {
- // onDraw will translate the canvas so we draw starting at 0,0
- int right = w - mPaddingRight - mPaddingLeft;
- int bottom = h - mPaddingBottom - mPaddingTop;
+ // onDraw will translate the canvas so we draw starting at 0,0.
+ // Subtract out padding for the purposes of the calculations below.
+ w -= mPaddingRight + mPaddingLeft;
+ h -= mPaddingTop + mPaddingBottom;
+
+ int right = w;
+ int bottom = h;
int top = 0;
int left = 0;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4656b8b..94e7a06 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2332,8 +2332,8 @@
batteryRealtime, which);
}
- @Override public int getPhoneSignalStrengthCount(int dataType, int which) {
- return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
+ @Override public int getPhoneSignalStrengthCount(int strengthBin, int which) {
+ return mPhoneSignalStrengthsTimer[strengthBin].getCountLocked(which);
}
@Override public long getPhoneDataConnectionTime(int dataType,
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 85e23fa..dc4ee3a 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -531,6 +531,9 @@
@Override
protected boolean hasDividerBeforeChildAt(int childIndex) {
+ if (childIndex == 0) {
+ return false;
+ }
final View childBefore = getChildAt(childIndex - 1);
final View child = getChildAt(childIndex);
boolean result = false;
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d6d2cf6..2108d0d 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -856,6 +856,7 @@
final int paddingRight = getPaddingRight();
final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ final int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
int availableWidth = contentWidth - paddingLeft - paddingRight;
int leftOfCenter = availableWidth / 2;
@@ -871,16 +872,14 @@
} else {
homeWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
}
- homeLayout.measure(homeWidthSpec,
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ homeLayout.measure(homeWidthSpec, exactHeightSpec);
final int homeWidth = homeLayout.getMeasuredWidth() + homeLayout.getStartOffset();
availableWidth = Math.max(0, availableWidth - homeWidth);
leftOfCenter = Math.max(0, availableWidth - homeWidth);
}
if (mMenuView != null && mMenuView.getParent() == this) {
- availableWidth = measureChildView(mMenuView, availableWidth,
- childSpecHeight, 0);
+ availableWidth = measureChildView(mMenuView, availableWidth, exactHeightSpec, 0);
rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth());
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 2bff8a1..9fc73a4 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -720,6 +720,16 @@
// Layers
// ----------------------------------------------------------------------------
+static void android_view_GLES20Canvas_interrupt(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer) {
+ renderer->interrupt();
+}
+
+static void android_view_GLES20Canvas_resume(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer) {
+ renderer->resume();
+}
+
static OpenGLRenderer* android_view_GLES20Canvas_createLayerRenderer(JNIEnv* env,
jobject clazz, Layer* layer) {
if (layer) {
@@ -962,6 +972,8 @@
{ "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer },
{ "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList },
+ { "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt },
+ { "nResume", "(I)V", (void*) android_view_GLES20Canvas_resume },
{ "nCreateLayerRenderer", "(I)I", (void*) android_view_GLES20Canvas_createLayerRenderer },
{ "nCreateLayer", "(IIZ[I)I", (void*) android_view_GLES20Canvas_createLayer },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d636713..195b1ef 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -278,6 +278,13 @@
android:label="@string/permlab_writeContacts"
android:description="@string/permdesc_writeContacts" />
+ <!-- Allows an application to execute contacts directory search.
+ This should only be used by ContactsProvider. -->
+ <!-- @hide -->
+ <permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
+ android:permissionGroup="android.permission-group.PERSONAL_INFO"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to read the user's call log. -->
<permission android:name="android.permission.READ_CALL_LOG"
android:permissionGroup="android.permission-group.PERSONAL_INFO"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8bc1e79..1c3318d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -284,7 +284,19 @@
who do not know their particular component name) and for all
content providers. -->
<attr name="exported" format="boolean" />
-
+
+ <!-- If set to true, a single instance of this component will run for
+ all users. That instance will run as user 0, the default/primary
+ user. When the app running is in processes for other users and interacts
+ with this component (by binding to a service for example) those processes will
+ always interact with the instance running for user 0. Enabling
+ single user mode forces "exported" of the component to be false, to
+ help avoid introducing multi-user security bugs. You must hold the
+ permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS} in order
+ to use this feature. This flag can only be used with services,
+ receivers, and providers; it can not be used with activities. -->
+ <attr name="singleUser" format="boolean" />
+
<!-- Specify a specific process that the associated code is to run in.
Use with the application tag (to supply a default process for all
application components), or with the activity, receiver, service,
@@ -1194,6 +1206,7 @@
component specific values). -->
<attr name="enabled" />
<attr name="exported" />
+ <attr name="singleUser" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
@@ -1275,16 +1288,7 @@
that is isolated from the rest of the system. The only communication
with it is through the Service API (binding and starting). -->
<attr name="isolatedProcess" format="boolean" />
- <!-- If set to true, a single instance of this service will run for
- all users. That instance will run as user 0, the default/primary
- user. When the app running in processes for other users interacts
- with this service (by binding to it, starting it, etc) they will
- always interact with the instance running for user 0. Enabling
- single user mode forces "exported" of the service to be false, to
- avoid introducing multi-user security bugs. You must hold the
- permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS} in order
- to use this feature. -->
- <attr name="singleUser" format="boolean" />
+ <attr name="singleUser" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
@@ -1318,8 +1322,9 @@
component specific values). -->
<attr name="enabled" />
<attr name="exported" />
+ <attr name="singleUser" />
</declare-styleable>
-
+
<!-- The <code>activity</code> tag declares an
{@link android.app.Activity} class that is available
as part of the package's application components, implementing
@@ -1372,6 +1377,7 @@
<attr name="hardwareAccelerated" />
<attr name="uiOptions" />
<attr name="parentActivityName" />
+ <attr name="singleUser" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b752471..a4bf43f 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -853,8 +853,6 @@
<!-- enable screen saver feature -->
<bool name="config_enableDreams">true</bool>
- <!-- Name of screensaver components to look for if none has been chosen by the user -->
- <string name="config_defaultDreamComponent" translatable="false">com.google.android.deskclock/com.android.deskclock.Screensaver</string>
<!-- Base "touch slop" value used by ViewConfiguration as a
movement threshold where scrolling should begin. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 41abd9b..965d02b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1557,7 +1557,6 @@
<java-symbol type="style" name="Theme.Toast" />
<java-symbol type="xml" name="storage_list" />
<java-symbol type="bool" name="config_enableDreams" />
- <java-symbol type="string" name="config_defaultDreamComponent" />
<java-symbol type="string" name="enable_explore_by_touch_warning_title" />
<java-symbol type="string" name="enable_explore_by_touch_warning_message" />
diff --git a/docs/html/guide/topics/manifest/provider-element.jd b/docs/html/guide/topics/manifest/provider-element.jd
index 4558800..7b4ca8f 100644
--- a/docs/html/guide/topics/manifest/provider-element.jd
+++ b/docs/html/guide/topics/manifest/provider-element.jd
@@ -97,7 +97,8 @@
applications — "{@code true}" if it can be, and "{@code false}" if not.
If "{@code false}", the provider is available only to components of the
same application or applications with the same user ID. The default value
-is "{@code true}".
+is "{@code true}" for applications which target API level 16 (Jelly Bean)
+and below, and "{@code false}" otherwise.
<p>
You can export a content provider but still limit access to it with the
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 483ccb2..31c38d5 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -26,20 +26,16 @@
import android.os.RemoteException;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
-import java.io.IOException;
import java.security.InvalidKeyException;
-import java.security.KeyPair;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
-import libcore.util.Objects;
import org.apache.harmony.xnet.provider.jsse.OpenSSLEngine;
import org.apache.harmony.xnet.provider.jsse.TrustedCertificateStore;
@@ -341,20 +337,9 @@
try {
IKeyChainService keyChainService = keyChainConnection.getService();
byte[] certificateBytes = keyChainService.getCertificate(alias);
- List<X509Certificate> chain = new ArrayList<X509Certificate>();
- chain.add(toCertificate(certificateBytes));
TrustedCertificateStore store = new TrustedCertificateStore();
- for (int i = 0; true; i++) {
- X509Certificate cert = chain.get(i);
- if (Objects.equal(cert.getSubjectX500Principal(), cert.getIssuerX500Principal())) {
- break;
- }
- X509Certificate issuer = store.findIssuer(cert);
- if (issuer == null) {
- break;
- }
- chain.add(issuer);
- }
+ List<X509Certificate> chain = store
+ .getCertificateChain(toCertificate(certificateBytes));
return chain.toArray(new X509Certificate[chain.size()]);
} catch (RemoteException e) {
throw new KeyChainException(e);
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 11f30fc..ccddd91 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -963,6 +963,11 @@
uint32_t FontRenderer::getRemainingCacheCapacity() {
uint32_t remainingCapacity = 0;
float totalPixels = 0;
+
+ //avoid divide by zero if the size is 0
+ if (mCacheLines.size() == 0) {
+ return 0;
+ }
for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
totalPixels += mCacheLines[i]->mMaxWidth;
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index b1c4dfe..726b57c7 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -18,6 +18,7 @@
#include <utils/threads.h>
+#include "Caches.h"
#include "Debug.h"
#include "GradientCache.h"
#include "Properties.h"
@@ -128,9 +129,13 @@
void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
GradientInfo& info) {
- uint32_t width = 1 << (31 - __builtin_clz(256 * (count - 1)));
- bool hasAlpha = false;
+ uint32_t width = 256 * (count - 1);
+ if (!Caches::getInstance().extensions.hasNPot()) {
+ width = 1 << (31 - __builtin_clz(width));
+ }
+
+ bool hasAlpha = false;
for (int i = 0; i < count; i++) {
if (((colors[i] >> 24) & 0xff) < 255) {
hasAlpha = true;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index f4e6919..d3b98a4 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -112,6 +112,21 @@
*/
virtual void finish();
+ /**
+ * This method must be invoked before handing control over to a draw functor.
+ * See callDrawGLFunction() for instance.
+ *
+ * This command must not be recorded inside display lists.
+ */
+ virtual void interrupt();
+
+ /**
+ * This method must be invoked after getting control back from a draw functor.
+ *
+ * This command must not be recorded inside display lists.
+ */
+ virtual void resume();
+
ANDROID_API status_t invokeFunctors(Rect& dirty);
ANDROID_API void detachFunctor(Functor* functor);
ANDROID_API void attachFunctor(Functor* functor);
@@ -219,22 +234,6 @@
void endMark() const;
protected:
-
- /**
- * This method must be invoked before handing control over to a draw functor.
- * See callDrawGLFunction() for instance.
- *
- * This command must not be recorded inside display lists.
- */
- void interrupt();
-
- /**
- * This method must be invoked after getting control back from a draw functor.
- *
- * This command must not be recorded inside display lists.
- */
- void resume();
-
/**
* Compose the layer defined in the current snapshot with the layer
* defined by the previous snapshot.
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 0e77cb2..d67bfbe 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -69,12 +69,10 @@
"varying highp vec2 outBitmapTexCoords;\n";
const char* gVS_Header_Varyings_PointHasBitmap =
"varying highp vec2 outPointBitmapTexCoords;\n";
-// TODO: These values are used to sample from textures,
-// they may need to be highp
const char* gVS_Header_Varyings_HasGradient[6] = {
// Linear
"varying highp vec2 linear;\n",
- "varying highp float linear;\n",
+ "varying float linear;\n",
// Circular
"varying highp vec2 circular;\n",
@@ -268,21 +266,21 @@
};
const char* gFS_Main_FetchGradient[6] = {
// Linear
- " highp vec4 gradientColor = texture2D(gradientSampler, linear);\n",
+ " vec4 gradientColor = texture2D(gradientSampler, linear);\n",
- " highp vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
+ " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
// Circular
- " highp vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
+ " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
- " highp vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
+ " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
// Sweep
" highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
- " highp vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
+ " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
" highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
- " highp vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
+ " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
" vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 9af201d..ca2df47 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -721,12 +721,17 @@
public native int getMaxAmplitude() throws IllegalStateException;
/* Do not change this value without updating its counterpart
- * in include/media/mediarecorder.h!
+ * in include/media/mediarecorder.h or mediaplayer.h!
*/
/** Unspecified media recorder error.
* @see android.media.MediaRecorder.OnErrorListener
*/
public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1;
+ /** Media server died. In this case, the application must release the
+ * MediaRecorder object and instantiate a new one.
+ * @see android.media.MediaRecorder.OnErrorListener
+ */
+ public static final int MEDIA_ERROR_SERVER_DIED = 100;
/**
* Interface definition for a callback to be invoked when an error
@@ -741,6 +746,7 @@
* @param what the type of error that has occurred:
* <ul>
* <li>{@link #MEDIA_RECORDER_ERROR_UNKNOWN}
+ * <li>{@link #MEDIA_ERROR_SERVER_DIED}
* </ul>
* @param extra an extra code, specific to the error type
*/
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index cd0da5a..a9de51f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -151,4 +151,12 @@
<!-- Number of retries for connecting to DHCP.
Value here is the same as WifiStateMachine.DEFAULT_MAX_DHCP_RETRIES -->
<integer name="def_max_dhcp_retries">9</integer>
+
+ <!-- Dreams (screen saver) default settings -->
+ <!-- Whether the feature is enabled when charging (Settings.Secure.SCREENSAVER_ENABLED) -->
+ <bool name="def_screensaver_enabled">true</bool>
+ <!-- Whether the feature activates when docked (SCREENSAVER_ACTIVATE_ON_DOCK) -->
+ <bool name="def_screensaver_activate_on_dock">true</bool>
+ <!-- ComponentName of the default screen saver (Settings.Secure.SCREENSAVER_COMPONENT) -->
+ <string name="def_screensaver_component">com.google.android.deskclock/com.android.deskclock.Screensaver</string>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 71c8c83..7c6ce73 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -65,7 +65,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 79;
+ private static final int DATABASE_VERSION = 80;
private Context mContext;
@@ -1073,6 +1073,30 @@
upgradeVersion = 79;
}
+ // vvv Jelly Bean MR1 changes begin here vvv
+
+ if (upgradeVersion == 79) {
+ // update screensaver settings
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
+ + " VALUES(?,?);");
+ loadBooleanSetting(stmt, Settings.Secure.SCREENSAVER_ENABLED,
+ R.bool.def_screensaver_enabled);
+ loadBooleanSetting(stmt, Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ R.bool.def_screensaver_activate_on_dock);
+ loadStringSetting(stmt, Settings.Secure.SCREENSAVER_COMPONENT,
+ R.string.def_screensaver_component);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 80;
+ }
+
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 10849f6..51dc3b1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -40,6 +40,7 @@
<uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
<uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
<uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<!-- WindowManager -->
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index fefd0e8..3e03f85 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -30,6 +30,7 @@
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Process;
+import android.os.UserId;
import android.util.Log;
import com.android.systemui.R;
@@ -243,7 +244,8 @@
mContext.getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+ am.getRecentTasksForUser(MAX_TASKS,
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserId.USER_CURRENT);
int numTasks = recentTasks.size();
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 16e9345..34474687 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -46,6 +46,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserId;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -2222,8 +2223,8 @@
ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
} catch (RemoteException e) {
}
- v.getContext().startActivity(new Intent(Settings.ACTION_SETTINGS)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ v.getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserId.USER_CURRENT);
animateCollapse();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
index 537ff66..da161a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
@@ -19,6 +19,7 @@
import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
+import android.os.UserId;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Slog;
@@ -117,8 +118,8 @@
// Settings
// ----------------------------
private void onClickSettings() {
- getContext().startActivity(new Intent(Settings.ACTION_SETTINGS)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserId.USER_CURRENT);
getStatusBarManager().collapse();
}
}
diff --git a/samples/training/network-usage/AndroidManifest.xml b/samples/training/network-usage/AndroidManifest.xml
new file mode 100644
index 0000000..4b96d14
--- /dev/null
+++ b/samples/training/network-usage/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<!--
+ Copyright (C) 2012 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.networkusage"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="4"
+ android:targetSdkVersion="14" />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+
+ <activity
+ android:name="com.example.networkusage.NetworkActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SettingsActivity" android:name=".SettingsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/training/network-usage/README.txt b/samples/training/network-usage/README.txt
new file mode 100644
index 0000000..cc9a7a0
--- /dev/null
+++ b/samples/training/network-usage/README.txt
@@ -0,0 +1,14 @@
+README
+======
+
+This Network Usage sample app does the following:
+
+-- Downloads an XML feed from StackOverflow.com for the most recent posts tagged "android".
+
+-- Parses the XML feed, combines feed elements with HTML markup, and displays the resulting HTML in the UI.
+
+-- Lets users control their network data usage through a settings UI. Users can choose to fetch the feed
+ when any network connection is available, or only when a Wi-Fi connection is available.
+
+-- Detects when there is a change in the device's connection status and responds accordingly. For example, if
+ the device loses its network connection, the app will not attempt to download the feed.
diff --git a/samples/training/network-usage/res/drawable-hdpi/ic_launcher.png b/samples/training/network-usage/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/samples/training/network-usage/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/network-usage/res/drawable-ldpi/ic_launcher.png b/samples/training/network-usage/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/samples/training/network-usage/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/network-usage/res/drawable-mdpi/ic_launcher.png b/samples/training/network-usage/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/samples/training/network-usage/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/network-usage/res/layout/main.xml b/samples/training/network-usage/res/layout/main.xml
new file mode 100644
index 0000000..8498934
--- /dev/null
+++ b/samples/training/network-usage/res/layout/main.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+<WebView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/webview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+/>
+</LinearLayout>
diff --git a/samples/training/network-usage/res/menu/mainmenu.xml b/samples/training/network-usage/res/menu/mainmenu.xml
new file mode 100644
index 0000000..17d44db
--- /dev/null
+++ b/samples/training/network-usage/res/menu/mainmenu.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2012 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.
+ -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/settings"
+ android:title="@string/settings" />
+ <item android:id="@+id/refresh"
+ android:title="@string/refresh" />
+</menu>
diff --git a/samples/training/network-usage/res/values/arrays.xml b/samples/training/network-usage/res/values/arrays.xml
new file mode 100644
index 0000000..2e8b8a7
--- /dev/null
+++ b/samples/training/network-usage/res/values/arrays.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2012 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.
+ -->
+
+<resources>
+ <string-array name="listArray">
+ <item>Only when on Wi-Fi</item>
+ <item>On any network</item>
+ </string-array>
+ <string-array name="listValues">
+ <item>Wi-Fi</item>
+ <item>Any</item>
+ </string-array>
+</resources>
diff --git a/samples/training/network-usage/res/values/strings.xml b/samples/training/network-usage/res/values/strings.xml
new file mode 100644
index 0000000..d7c702f
--- /dev/null
+++ b/samples/training/network-usage/res/values/strings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2012 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.
+ -->
+
+<resources>
+
+ <string name="app_name">NetworkUsage</string>
+
+ <!-- Menu items -->
+ <string name="settings">Settings</string>
+ <string name="refresh">Refresh</string>
+
+ <!-- NetworkActivity -->
+ <string name="page_title">Newest StackOverflow questions tagged \'android\'</string>
+ <string name="updated">Last updated:</string>
+ <string name="lost_connection">Lost connection.</string>
+ <string name="wifi_connected">Wi-Fi reconnected.</string>
+ <string name="connection_error">Unable to load content. Check your network connection.</string>
+ <string name="xml_error">Error parsing XML.</string>
+
+</resources>
diff --git a/samples/training/network-usage/res/xml/preferences.xml b/samples/training/network-usage/res/xml/preferences.xml
new file mode 100644
index 0000000..801ba79
--- /dev/null
+++ b/samples/training/network-usage/res/xml/preferences.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2012 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.
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <ListPreference
+ android:title="Download Feed"
+ android:summary="Network connectivity required to download the feed."
+ android:key="listPref"
+ android:defaultValue="Wi-Fi"
+ android:entries="@array/listArray"
+ android:entryValues="@array/listValues"
+ />
+ <CheckBoxPreference
+ android:title="Show Summaries"
+ android:defaultValue="false"
+ android:summary="Show a summary for each link."
+ android:key="summaryPref" />
+</PreferenceScreen>
diff --git a/samples/training/network-usage/src/com/example/android/networkusage/NetworkActivity.java b/samples/training/network-usage/src/com/example/android/networkusage/NetworkActivity.java
new file mode 100644
index 0000000..b7ed331
--- /dev/null
+++ b/samples/training/network-usage/src/com/example/android/networkusage/NetworkActivity.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2012 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.example.android.networkusage;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.webkit.WebView;
+import android.widget.Toast;
+
+import com.example.android.networkusage.R;
+import com.example.android.networkusage.StackOverflowXmlParser.Entry;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.List;
+
+
+/**
+ * Main Activity for the sample application.
+ *
+ * This activity does the following:
+ *
+ * o Presents a WebView screen to users. This WebView has a list of HTML links to the latest
+ * questions tagged 'android' on stackoverflow.com.
+ *
+ * o Parses the StackOverflow XML feed using XMLPullParser.
+ *
+ * o Uses AsyncTask to download and process the XML feed.
+ *
+ * o Monitors preferences and the device's network connection to determine whether
+ * to refresh the WebView content.
+ */
+public class NetworkActivity extends Activity {
+ public static final String WIFI = "Wi-Fi";
+ public static final String ANY = "Any";
+ private static final String URL =
+ "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
+
+ // Whether there is a Wi-Fi connection.
+ private static boolean wifiConnected = false;
+ // Whether there is a mobile connection.
+ private static boolean mobileConnected = false;
+ // Whether the display should be refreshed.
+ public static boolean refreshDisplay = true;
+
+ // The user's current network preference setting.
+ public static String sPref = null;
+
+ // The BroadcastReceiver that tracks network connectivity changes.
+ private NetworkReceiver receiver = new NetworkReceiver();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Register BroadcastReceiver to track connection changes.
+ IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+ receiver = new NetworkReceiver();
+ this.registerReceiver(receiver, filter);
+ }
+
+ // Refreshes the display if the network connection and the
+ // pref settings allow it.
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // Gets the user's network preference settings
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Retrieves a string value for the preferences. The second parameter
+ // is the default value to use if a preference value is not found.
+ sPref = sharedPrefs.getString("listPref", "Wi-Fi");
+
+ updateConnectedFlags();
+
+ // Only loads the page if refreshDisplay is true. Otherwise, keeps previous
+ // display. For example, if the user has set "Wi-Fi only" in prefs and the
+ // device loses its Wi-Fi connection midway through the user using the app,
+ // you don't want to refresh the display--this would force the display of
+ // an error page instead of stackoverflow.com content.
+ if (refreshDisplay) {
+ loadPage();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (receiver != null) {
+ this.unregisterReceiver(receiver);
+ }
+ }
+
+ // Checks the network connection and sets the wifiConnected and mobileConnected
+ // variables accordingly.
+ private void updateConnectedFlags() {
+ ConnectivityManager connMgr =
+ (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
+ if (activeInfo != null && activeInfo.isConnected()) {
+ wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
+ mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
+ } else {
+ wifiConnected = false;
+ mobileConnected = false;
+ }
+ }
+
+ // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
+ // This avoids UI lock up. To prevent network operations from
+ // causing a delay that results in a poor user experience, always perform
+ // network operations on a separate thread from the UI.
+ private void loadPage() {
+ if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
+ || ((sPref.equals(WIFI)) && (wifiConnected))) {
+ // AsyncTask subclass
+ new DownloadXmlTask().execute(URL);
+ } else {
+ showErrorPage();
+ }
+ }
+
+ // Displays an error if the app is unable to load content.
+ private void showErrorPage() {
+ setContentView(R.layout.main);
+
+ // The specified network connection is not available. Displays error message.
+ WebView myWebView = (WebView) findViewById(R.id.webview);
+ myWebView.loadData(getResources().getString(R.string.connection_error),
+ "text/html", null);
+ }
+
+ // Populates the activity's options menu.
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.mainmenu, menu);
+ return true;
+ }
+
+ // Handles the user's menu selection.
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.settings:
+ Intent settingsActivity = new Intent(getBaseContext(), SettingsActivity.class);
+ startActivity(settingsActivity);
+ return true;
+ case R.id.refresh:
+ loadPage();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ // Implementation of AsyncTask used to download XML feed from stackoverflow.com.
+ private class DownloadXmlTask extends AsyncTask<String, Void, String> {
+
+ @Override
+ protected String doInBackground(String... urls) {
+ try {
+ return loadXmlFromNetwork(urls[0]);
+ } catch (IOException e) {
+ return getResources().getString(R.string.connection_error);
+ } catch (XmlPullParserException e) {
+ return getResources().getString(R.string.xml_error);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ setContentView(R.layout.main);
+ // Displays the HTML string in the UI via a WebView
+ WebView myWebView = (WebView) findViewById(R.id.webview);
+ myWebView.loadData(result, "text/html", null);
+ }
+ }
+
+ // Uploads XML from stackoverflow.com, parses it, and combines it with
+ // HTML markup. Returns HTML string.
+ private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
+ InputStream stream = null;
+ StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
+ List<Entry> entries = null;
+ String title = null;
+ String url = null;
+ String summary = null;
+ Calendar rightNow = Calendar.getInstance();
+ DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");
+
+ // Checks whether the user set the preference to include summary text
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean pref = sharedPrefs.getBoolean("summaryPref", false);
+
+ StringBuilder htmlString = new StringBuilder();
+ htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
+ htmlString.append("<em>" + getResources().getString(R.string.updated) + " " +
+ formatter.format(rightNow.getTime()) + "</em>");
+
+ try {
+ stream = downloadUrl(urlString);
+ entries = stackOverflowXmlParser.parse(stream);
+ // Makes sure that the InputStream is closed after the app is
+ // finished using it.
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+
+ // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
+ // Each Entry object represents a single post in the XML feed.
+ // This section processes the entries list to combine each entry with HTML markup.
+ // Each entry is displayed in the UI as a link that optionally includes
+ // a text summary.
+ for (Entry entry : entries) {
+ htmlString.append("<p><a href='");
+ htmlString.append(entry.link);
+ htmlString.append("'>" + entry.title + "</a></p>");
+ // If the user set the preference to include summary text,
+ // adds it to the display.
+ if (pref) {
+ htmlString.append(entry.summary);
+ }
+ }
+ return htmlString.toString();
+ }
+
+ // Given a string representation of a URL, sets up a connection and gets
+ // an input stream.
+ private InputStream downloadUrl(String urlString) throws IOException {
+ URL url = new URL(urlString);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(10000 /* milliseconds */);
+ conn.setConnectTimeout(15000 /* milliseconds */);
+ conn.setRequestMethod("GET");
+ conn.setDoInput(true);
+ // Starts the query
+ conn.connect();
+ InputStream stream = conn.getInputStream();
+ return stream;
+ }
+
+ /**
+ *
+ * This BroadcastReceiver intercepts the android.net.ConnectivityManager.CONNECTIVITY_ACTION,
+ * which indicates a connection change. It checks whether the type is TYPE_WIFI.
+ * If it is, it checks whether Wi-Fi is connected and sets the wifiConnected flag in the
+ * main activity accordingly.
+ *
+ */
+ public class NetworkReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ConnectivityManager connMgr =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
+
+ // Checks the user prefs and the network connection. Based on the result, decides
+ // whether
+ // to refresh the display or keep the current display.
+ // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
+ if (WIFI.equals(sPref) && networkInfo != null
+ && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ // If device has its Wi-Fi connection, sets refreshDisplay
+ // to true. This causes the display to be refreshed when the user
+ // returns to the app.
+ refreshDisplay = true;
+ Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
+
+ // If the setting is ANY network and there is a network connection
+ // (which by process of elimination would be mobile), sets refreshDisplay to true.
+ } else if (ANY.equals(sPref) && networkInfo != null) {
+ refreshDisplay = true;
+
+ // Otherwise, the app can't download content--either because there is no network
+ // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
+ // is no Wi-Fi connection.
+ // Sets refreshDisplay to false.
+ } else {
+ refreshDisplay = false;
+ Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+}
diff --git a/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java b/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java
new file mode 100644
index 0000000..73b72d2
--- /dev/null
+++ b/samples/training/network-usage/src/com/example/android/networkusage/SettingsActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 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.example.android.networkusage;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import com.example.android.networkusage.R;
+
+/**
+ * This preference activity has in its manifest declaration an intent filter for
+ * the ACTION_MANAGE_NETWORK_USAGE action. This activity provides a settings UI
+ * for users to specify network settings to control data usage.
+ */
+public class SettingsActivity extends PreferenceActivity
+ implements
+ OnSharedPreferenceChangeListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Loads the XML preferences file.
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Registers a callback to be invoked whenever a user changes a preference.
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // Unregisters the listener set in onResume().
+ // It's best practice to unregister listeners when your app isn't using them to cut down on
+ // unnecessary system overhead. You do this in onPause().
+ getPreferenceScreen()
+ .getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ // Fires when the user changes a preference.
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ // Sets refreshDisplay to true so that when the user returns to the main
+ // activity, the display refreshes to reflect the new settings.
+ NetworkActivity.refreshDisplay = true;
+ }
+}
diff --git a/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java b/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java
new file mode 100644
index 0000000..6a01098
--- /dev/null
+++ b/samples/training/network-usage/src/com/example/android/networkusage/StackOverflowXmlParser.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 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.example.android.networkusage;
+
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class parses XML feeds from stackoverflow.com.
+ * Given an InputStream representation of a feed, it returns a List of entries,
+ * where each list element represents a single entry (post) in the XML feed.
+ */
+public class StackOverflowXmlParser {
+ private static final String ns = null;
+
+ // We don't use namespaces
+
+ public List<Entry> parse(InputStream in) throws XmlPullParserException, IOException {
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ parser.setInput(in, null);
+ parser.nextTag();
+ return readFeed(parser);
+ } finally {
+ in.close();
+ }
+ }
+
+ private List<Entry> readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
+ List<Entry> entries = new ArrayList<Entry>();
+
+ parser.require(XmlPullParser.START_TAG, ns, "feed");
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ // Starts by looking for the entry tag
+ if (name.equals("entry")) {
+ entries.add(readEntry(parser));
+ } else {
+ skip(parser);
+ }
+ }
+ return entries;
+ }
+
+ // This class represents a single entry (post) in the XML feed.
+ // It includes the data members "title," "link," and "summary."
+ public static class Entry {
+ public final String title;
+ public final String link;
+ public final String summary;
+
+ private Entry(String title, String summary, String link) {
+ this.title = title;
+ this.summary = summary;
+ this.link = link;
+ }
+ }
+
+ // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them
+ // off
+ // to their respective "read" methods for processing. Otherwise, skips the tag.
+ private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
+ parser.require(XmlPullParser.START_TAG, ns, "entry");
+ String title = null;
+ String summary = null;
+ String link = null;
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ if (name.equals("title")) {
+ title = readTitle(parser);
+ } else if (name.equals("summary")) {
+ summary = readSummary(parser);
+ } else if (name.equals("link")) {
+ link = readLink(parser);
+ } else {
+ skip(parser);
+ }
+ }
+ return new Entry(title, summary, link);
+ }
+
+ // Processes title tags in the feed.
+ private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, ns, "title");
+ String title = readText(parser);
+ parser.require(XmlPullParser.END_TAG, ns, "title");
+ return title;
+ }
+
+ // Processes link tags in the feed.
+ private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
+ String link = "";
+ parser.require(XmlPullParser.START_TAG, ns, "link");
+ String tag = parser.getName();
+ String relType = parser.getAttributeValue(null, "rel");
+ if (tag.equals("link")) {
+ if (relType.equals("alternate")) {
+ link = parser.getAttributeValue(null, "href");
+ parser.nextTag();
+ }
+ }
+ parser.require(XmlPullParser.END_TAG, ns, "link");
+ return link;
+ }
+
+ // Processes summary tags in the feed.
+ private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, ns, "summary");
+ String summary = readText(parser);
+ parser.require(XmlPullParser.END_TAG, ns, "summary");
+ return summary;
+ }
+
+ // For the tags title and summary, extracts their text values.
+ private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
+ String result = "";
+ if (parser.next() == XmlPullParser.TEXT) {
+ result = parser.getText();
+ parser.nextTag();
+ }
+ return result;
+ }
+
+ // Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e.,
+ // if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it
+ // finds the matching END_TAG (as indicated by the value of "depth" being 0).
+ private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ throw new IllegalStateException();
+ }
+ int depth = 1;
+ while (depth != 0) {
+ switch (parser.next()) {
+ case XmlPullParser.END_TAG:
+ depth--;
+ break;
+ case XmlPullParser.START_TAG:
+ depth++;
+ break;
+ }
+ }
+ }
+}
diff --git a/services/common_time/common_time_server.cpp b/services/common_time/common_time_server.cpp
index 16be8f1..17f99b9 100644
--- a/services/common_time/common_time_server.cpp
+++ b/services/common_time/common_time_server.cpp
@@ -584,7 +584,7 @@
static void hexDumpToString(const uint8_t* src, size_t src_len,
char* dst, size_t dst_len) {
- size_t offset;
+ size_t offset = 0;
size_t i;
for (i = 0; (i < src_len) && (offset < dst_len); ++i) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index dc0cb75..6049b05 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -20,6 +20,13 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
+import static android.net.ConnectivityManager.TYPE_DUMMY;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -86,14 +93,13 @@
import com.android.server.net.BaseNetworkObserver;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+
import dalvik.system.DexClassLoader;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationTargetException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -317,6 +323,14 @@
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ // Currently, omitting a NetworkFactory will create one internally
+ // TODO: create here when we have cleaner WiMAX support
+ this(context, netd, statsService, policyManager, null);
+ }
+
+ public ConnectivityService(Context context, INetworkManagementService netd,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager,
+ NetworkFactory netFactory) {
if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -324,6 +338,10 @@
mHandler = new InternalHandler(handlerThread.getLooper());
mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper());
+ if (netFactory == null) {
+ netFactory = new DefaultNetworkFactory(context, mTrackerHandler);
+ }
+
// setup our unique device name
if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
String id = Settings.Secure.getString(context.getContentResolver(),
@@ -462,59 +480,27 @@
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
- /*
- * Create the network state trackers for Wi-Fi and mobile
- * data. Maybe this could be done with a factory class,
- * but it's not clear that it's worth it, given that
- * the number of different network types is not going
- * to change very often.
- */
- for (int netType : mPriorityList) {
- switch (mNetConfigs[netType].radio) {
- case ConnectivityManager.TYPE_WIFI:
- mNetTrackers[netType] = new WifiStateTracker(
- netType, mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mTrackerHandler);
- break;
- case ConnectivityManager.TYPE_MOBILE:
- mNetTrackers[netType] = new MobileDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mTrackerHandler);
- break;
- case ConnectivityManager.TYPE_DUMMY:
- mNetTrackers[netType] = new DummyDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mTrackerHandler);
- break;
- case ConnectivityManager.TYPE_BLUETOOTH:
- mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mTrackerHandler);
- break;
- case ConnectivityManager.TYPE_WIMAX:
- mNetTrackers[netType] = makeWimaxStateTracker();
- if (mNetTrackers[netType]!= null) {
- mNetTrackers[netType].startMonitoring(context, mTrackerHandler);
- }
- break;
- case ConnectivityManager.TYPE_ETHERNET:
- mNetTrackers[netType] = EthernetDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mTrackerHandler);
- break;
- default:
- loge("Trying to create a DataStateTracker for an unknown radio type " +
- mNetConfigs[netType].radio);
+
+ // Create and start trackers for hard-coded networks
+ for (int targetNetworkType : mPriorityList) {
+ final NetworkConfig config = mNetConfigs[targetNetworkType];
+ final NetworkStateTracker tracker;
+ try {
+ tracker = netFactory.createTracker(targetNetworkType, config);
+ mNetTrackers[targetNetworkType] = tracker;
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Problem creating " + getNetworkTypeName(targetNetworkType)
+ + " tracker: " + e);
continue;
}
- mCurrentLinkProperties[netType] = null;
- if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {
- mNetTrackers[netType].reconnect();
+
+ tracker.startMonitoring(context, mTrackerHandler);
+ if (config.isDefault()) {
+ tracker.reconnect();
}
}
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
-
- mTethering = new Tethering(mContext, nmService, statsService, this, mHandler.getLooper());
+ mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
@@ -523,9 +509,9 @@
mVpn = new Vpn(mContext, new VpnCallback());
try {
- nmService.registerObserver(mTethering);
- nmService.registerObserver(mVpn);
- nmService.registerObserver(mDataActivityObserver);
+ mNetd.registerObserver(mTethering);
+ mNetd.registerObserver(mVpn);
+ mNetd.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -540,7 +526,53 @@
loadGlobalProxy();
}
- private NetworkStateTracker makeWimaxStateTracker() {
+ /**
+ * Factory that creates {@link NetworkStateTracker} instances using given
+ * {@link NetworkConfig}.
+ */
+ public interface NetworkFactory {
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config);
+ }
+
+ private static class DefaultNetworkFactory implements NetworkFactory {
+ private final Context mContext;
+ private final Handler mTrackerHandler;
+
+ public DefaultNetworkFactory(Context context, Handler trackerHandler) {
+ mContext = context;
+ mTrackerHandler = trackerHandler;
+ }
+
+ @Override
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
+ switch (config.radio) {
+ case TYPE_WIFI:
+ return new WifiStateTracker(targetNetworkType, config.name);
+ case TYPE_MOBILE:
+ return new MobileDataStateTracker(targetNetworkType, config.name);
+ case TYPE_DUMMY:
+ return new DummyDataStateTracker(targetNetworkType, config.name);
+ case TYPE_BLUETOOTH:
+ return BluetoothTetheringDataTracker.getInstance();
+ case TYPE_WIMAX:
+ return makeWimaxStateTracker(mContext, mTrackerHandler);
+ case TYPE_ETHERNET:
+ return EthernetDataTracker.getInstance();
+ default:
+ throw new IllegalArgumentException(
+ "Trying to create a NetworkStateTracker for an unknown radio type: "
+ + config.radio);
+ }
+ }
+ }
+
+ /**
+ * Loads external WiMAX library and registers as system service, returning a
+ * {@link NetworkStateTracker} for WiMAX. Caller is still responsible for
+ * invoking {@link NetworkStateTracker#startMonitoring(Context, Handler)}.
+ */
+ private static NetworkStateTracker makeWimaxStateTracker(
+ Context context, Handler trackerHandler) {
// Initialize Wimax
DexClassLoader wimaxClassLoader;
Class wimaxStateTrackerClass = null;
@@ -554,25 +586,25 @@
NetworkStateTracker wimaxStateTracker = null;
- boolean isWimaxEnabled = mContext.getResources().getBoolean(
+ boolean isWimaxEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_wimaxEnabled);
if (isWimaxEnabled) {
try {
- wimaxJarLocation = mContext.getResources().getString(
+ wimaxJarLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceJarLocation);
- wimaxLibLocation = mContext.getResources().getString(
+ wimaxLibLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxNativeLibLocation);
- wimaxManagerClassName = mContext.getResources().getString(
+ wimaxManagerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxManagerClassname);
- wimaxServiceClassName = mContext.getResources().getString(
+ wimaxServiceClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceClassname);
- wimaxStateTrackerClassName = mContext.getResources().getString(
+ wimaxStateTrackerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxStateTrackerClassname);
log("wimaxJarLocation: " + wimaxJarLocation);
wimaxClassLoader = new DexClassLoader(wimaxJarLocation,
- new ContextWrapper(mContext).getCacheDir().getAbsolutePath(),
+ new ContextWrapper(context).getCacheDir().getAbsolutePath(),
wimaxLibLocation, ClassLoader.getSystemClassLoader());
try {
@@ -593,13 +625,13 @@
Constructor wmxStTrkrConst = wimaxStateTrackerClass.getConstructor
(new Class[] {Context.class, Handler.class});
- wimaxStateTracker = (NetworkStateTracker)wmxStTrkrConst.newInstance(mContext,
- mTrackerHandler);
+ wimaxStateTracker = (NetworkStateTracker) wmxStTrkrConst.newInstance(
+ context, trackerHandler);
Constructor wmxSrvConst = wimaxServiceClass.getDeclaredConstructor
(new Class[] {Context.class, wimaxStateTrackerClass});
wmxSrvConst.setAccessible(true);
- IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(mContext, wimaxStateTracker);
+ IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(context, wimaxStateTracker);
wmxSrvConst.setAccessible(false);
ServiceManager.addService(WimaxManagerConstants.WIMAX_SERVICE, svcInvoker);
@@ -1876,6 +1908,7 @@
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
final NetworkStateTracker thisNet = mNetTrackers[type];
+ final String thisIface = thisNet.getLinkProperties().getInterfaceName();
// if this is a default net and other default is running
// kill the one not preferred
@@ -1934,10 +1967,9 @@
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
// notify battery stats service about this network
- final String iface = thisNet.getLinkProperties().getInterfaceName();
- if (iface != null) {
+ if (thisIface != null) {
try {
- BatteryStatsService.getService().noteNetworkInterfaceType(iface, type);
+ BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, type);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -2927,11 +2959,11 @@
}
}
- private void log(String s) {
+ private static void log(String s) {
Slog.d(TAG, s);
}
- private void loge(String s) {
+ private static void loge(String s) {
Slog.e(TAG, s);
}
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 8bac52c..6f050d3 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;
+
import com.android.server.power.PowerManagerService;
import android.bluetooth.BluetoothAdapter;
@@ -51,6 +53,8 @@
private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
+ private static final int DEFAULT_DOCK = 1;
+
private static final int MSG_DOCK_STATE = 0;
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -131,6 +135,11 @@
mHandler.sendEmptyMessage(MSG_DOCK_STATE);
}
+ private static boolean isScreenSaverActivatedOnDock(Context context) {
+ return 0 != Settings.Secure.getInt(
+ context.getContentResolver(), SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_DOCK);
+ }
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -210,10 +219,12 @@
Slog.w(TAG, "Unable to awaken!", e);
}
} else {
- try {
- mgr.dream();
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to dream!", e);
+ if (isScreenSaverActivatedOnDock(mContext)) {
+ try {
+ mgr.dream();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to dream!", e);
+ }
}
}
} else {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 81f0733..96cf3d3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -702,6 +702,12 @@
}
try {
+ lockSettings.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Lock Settings Service ready", e);
+ }
+
+ try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
@@ -726,11 +732,6 @@
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
- try {
- lockSettings.systemReady();
- } catch (Throwable e) {
- reportWtf("making Lock Settings Service ready", e);
- }
// These are needed to propagate to the runnable below.
final Context contextF = context;
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index fbbff4c..df6c51e 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -478,7 +478,8 @@
if (res.record == null) {
return -1;
}
- if (mAm.isSingleton(res.record.processName, res.record.appInfo)) {
+ if (mAm.isSingleton(res.record.processName, res.record.appInfo,
+ res.record.serviceInfo.name, res.record.serviceInfo.flags)) {
userId = 0;
res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),
Binder.getCallingUid(), 0);
@@ -787,22 +788,9 @@
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
if (userId > 0) {
- if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo)
- || (sInfo.flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
+ if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
+ sInfo.name, sInfo.flags)) {
userId = 0;
- } else if ((sInfo.flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
- if (mAm.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS,
- callingPid, callingUid, -1, true)
- == PackageManager.PERMISSION_GRANTED) {
- userId = 0;
- } else {
- String msg = "Permission Denial: Service " + name
- + " requests FLAG_SINGLE_USER, but app does not hold "
- + android.Manifest.permission.INTERACT_ACROSS_USERS;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
}
sInfo = new ServiceInfo(sInfo);
sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 98d3482..375f7f1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -2305,19 +2305,48 @@
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags,
String profileFile, ParcelFileDescriptor profileFd, Bundle options) {
+ return startActivityAsUser(caller, intent, resolvedType, resultTo, resultWho, requestCode,
+ startFlags, profileFile, profileFd, options, UserId.getCallingUserId());
+ }
+
+ public final int startActivityAsUser(IApplicationThread caller,
+ Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, int startFlags,
+ String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
- int userId = 0;
- if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
- // Requesting home, set the identity to the current user
- // HACK!
- userId = mCurrentUserId;
- } else {
- // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
- // the current user's userId
- if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
- userId = 0;
+ if (userId != UserId.getCallingUserId()) {
+ // Requesting a different user, make sure that they have the permission
+ if (checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
+ == PackageManager.PERMISSION_GRANTED) {
+ // Translate to the current user id, if caller wasn't aware
+ if (userId == UserId.USER_CURRENT) {
+ userId = mCurrentUserId;
+ }
} else {
- userId = Binder.getOrigCallingUser();
+ String msg = "Permission Denial: "
+ + "Request to startActivity as user " + userId
+ + " but is calling from user " + UserId.getCallingUserId()
+ + "; this requires "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ } else {
+ if (intent.getCategories() != null
+ && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
+ // Requesting home, set the identity to the current user
+ // HACK!
+ userId = mCurrentUserId;
+ } else {
+ // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
+ // the current user's userId
+ if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
+ userId = 0;
+ } else {
+ userId = Binder.getOrigCallingUser();
+ }
}
}
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
@@ -5470,13 +5499,28 @@
}
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
- int flags) {
+ int flags, int userId) {
final int callingUid = Binder.getCallingUid();
- // If it's the system uid asking, then use the current user id.
- // TODO: Make sure that there aren't any other legitimate calls from the system uid that
- // require the entire list.
- final int callingUserId = callingUid == Process.SYSTEM_UID
- ? mCurrentUserId : UserId.getUserId(callingUid);
+ if (userId != UserId.getCallingUserId()) {
+ // Check if the caller is holding permissions for cross-user requests.
+ if (checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingPid(), callingUid, -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: "
+ + "Request to get recent tasks for user " + userId
+ + " but is calling from user " + UserId.getUserId(callingUid)
+ + "; this requires "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ } else {
+ if (userId == UserId.USER_CURRENT) {
+ userId = mCurrentUserId;
+ }
+ }
+ }
+
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
@@ -5485,7 +5529,7 @@
== PackageManager.PERMISSION_GRANTED;
IPackageManager pm = AppGlobals.getPackageManager();
-
+
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
= new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -5493,7 +5537,7 @@
for (int i=0; i<N && maxNum > 0; i++) {
TaskRecord tr = mRecentTasks.get(i);
// Only add calling user's recent tasks
- if (tr.userId != callingUserId) continue;
+ if (tr.userId != userId) continue;
// Return the entry if desired by the caller. We always return
// the first entry, because callers always expect this to be the
// foreground app. We may filter others if the caller has
@@ -5521,13 +5565,13 @@
// Check whether this activity is currently available.
try {
if (rti.origActivity != null) {
- if (pm.getActivityInfo(rti.origActivity, 0, callingUserId)
+ if (pm.getActivityInfo(rti.origActivity, 0, userId)
== null) {
continue;
}
} else if (rti.baseIntent != null) {
if (pm.queryIntentActivities(rti.baseIntent,
- null, 0, callingUserId) == null) {
+ null, 0, userId) == null) {
continue;
}
}
@@ -5936,15 +5980,26 @@
Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
int userId = app.userId;
if (providers != null) {
- final int N = providers.size();
+ int N = providers.size();
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
+ boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags);
+ if (singleton && UserId.getUserId(app.uid) != 0) {
+ // This is a singleton provider, but a user besides the
+ // default user is asking to initialize a process it runs
+ // in... well, no, it doesn't actually run in this process,
+ // it runs in the process of the default user. Get rid of it.
+ providers.remove(i);
+ N--;
+ continue;
+ }
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
- cpr = new ContentProviderRecord(this, cpi, app.info, comp);
+ cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
mProviderMap.putProviderByClass(comp, cpr);
}
if (DEBUG_MU)
@@ -6177,6 +6232,7 @@
Binder.restoreCallingIdentity(origId);
}
+ boolean singleton;
if (!providerRunning) {
try {
cpi = AppGlobals.getPackageManager().
@@ -6187,7 +6243,9 @@
if (cpi == null) {
return null;
}
- if (isSingleton(cpi.processName, cpi.applicationInfo)) {
+ singleton = isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags);
+ if (singleton) {
userId = 0;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
@@ -6222,7 +6280,7 @@
return null;
}
ai = getAppInfoForUser(ai, userId);
- cpr = new ContentProviderRecord(this, cpi, ai, comp);
+ cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
@@ -10515,17 +10573,31 @@
}
}
- boolean isSingleton(String componentProcessName, ApplicationInfo aInfo) {
+ boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
+ String className, int flags) {
boolean result = false;
if (UserId.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
- result = false;
+ if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ aInfo.uid) != PackageManager.PERMISSION_GRANTED) {
+ ComponentName comp = new ComponentName(aInfo.packageName, className);
+ String msg = "Permission Denial: Component " + comp.flattenToShortString()
+ + " requests FLAG_SINGLE_USER, but app does not hold "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ result = true;
+ }
} else if (componentProcessName == aInfo.packageName) {
result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
} else if ("system".equals(componentProcessName)) {
result = true;
}
if (DEBUG_MU) {
- Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo + ") = " + result);
+ Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo
+ + ", " + className + ", 0x" + Integer.toHexString(flags) + ") = " + result);
}
return result;
}
@@ -11131,30 +11203,15 @@
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
try {
- if (intent.getComponent() != null) {
- // Broadcast is going to one specific receiver class...
- ActivityInfo ai = AppGlobals.getPackageManager().
- getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS, userId);
- if (ai != null) {
- receivers = new ArrayList();
- ResolveInfo ri = new ResolveInfo();
- if (isSingleton(ai.processName, ai.applicationInfo)) {
- ri.activityInfo = getActivityInfoForUser(ai, 0);
- } else {
- ri.activityInfo = getActivityInfoForUser(ai, userId);
- }
- receivers.add(ri);
- }
- } else {
- // Need to resolve the intent to interested receivers...
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
- == 0) {
- receivers =
- AppGlobals.getPackageManager().queryIntentReceivers(
- intent, resolvedType, STOCK_PM_FLAGS, userId);
- }
- registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false,
- userId);
+ // Need to resolve the intent to interested receivers...
+ if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ == 0) {
+ receivers = AppGlobals.getPackageManager().queryIntentReceivers(
+ intent, resolvedType, STOCK_PM_FLAGS, userId);
+ }
+ if (intent.getComponent() == null) {
+ registeredReceivers = mReceiverResolver.queryIntent(intent,
+ resolvedType, false, userId);
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c9a633e..196a259 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -3014,7 +3014,8 @@
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
profileFile, profileFd, userId);
- if (aInfo != null && mService.isSingleton(aInfo.processName, aInfo.applicationInfo)) {
+ if (aInfo != null && mService.isSingleton(aInfo.processName, aInfo.applicationInfo,
+ null, 0)) {
userId = 0;
}
aInfo = mService.getActivityInfoForUser(aInfo, userId);
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index c6d46fc..76ddb96 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -20,12 +20,15 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -249,7 +252,7 @@
finishReceiverLocked(br, br.resultCode, br.resultData,
br.resultExtras, br.resultAbort, true);
scheduleBroadcastsLocked();
- // We need to reset the state if we fails to start the receiver.
+ // We need to reset the state if we failed to start the receiver.
br.state = BroadcastRecord.IDLE;
throw new RuntimeException(e.getMessage());
}
@@ -659,6 +662,9 @@
ResolveInfo info =
(ResolveInfo)nextReceiver;
+ ComponentName component = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
boolean skip = false;
if (r.onlySendToCaller) {
@@ -667,6 +673,7 @@
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
+ r.callingPid + ", uid=" + r.callingUid + ")"
+ + " to " + component.flattenToShortString()
+ " not allowed to go to different app "
+ info.activityInfo.applicationInfo.uid);
skip = true;
@@ -682,16 +689,14 @@
+ " from " + r.callerPackage + " (pid=" + r.callingPid
+ ", uid=" + r.callingUid + ")"
+ " is not exported from uid " + info.activityInfo.applicationInfo.uid
- + " due to receiver " + info.activityInfo.packageName
- + "/" + info.activityInfo.name);
+ + " due to receiver " + component.flattenToShortString());
} else {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid=" + r.callingPid
+ ", uid=" + r.callingUid + ")"
+ " requires " + info.activityInfo.permission
- + " due to receiver " + info.activityInfo.packageName
- + "/" + info.activityInfo.name);
+ + " due to receiver " + component.flattenToShortString());
}
skip = true;
}
@@ -707,13 +712,33 @@
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: receiving "
+ r.intent + " to "
- + info.activityInfo.applicationInfo.packageName
+ + component.flattenToShortString()
+ " requires " + r.requiredPermission
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")");
skip = true;
}
}
+ boolean isSingleton = false;
+ try {
+ isSingleton = mService.isSingleton(info.activityInfo.processName,
+ info.activityInfo.applicationInfo,
+ info.activityInfo.name, info.activityInfo.flags);
+ } catch (SecurityException e) {
+ Slog.w(TAG, e.getMessage());
+ skip = true;
+ }
+ if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ info.activityInfo.applicationInfo.uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
+ + " requests FLAG_SINGLE_USER, but app does not hold "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS);
+ skip = true;
+ }
+ }
if (r.curApp != null && r.curApp.crashing) {
// If the target process is crashing, just skip it.
if (DEBUG_BROADCAST) Slog.v(TAG,
@@ -736,14 +761,9 @@
r.state = BroadcastRecord.APP_RECEIVE;
String targetProcess = info.activityInfo.processName;
- r.curComponent = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
- if (r.callingUid != Process.SYSTEM_UID) {
- boolean isSingleton = mService.isSingleton(info.activityInfo.processName,
- info.activityInfo.applicationInfo);
- int targetUserId = isSingleton ? 0 : UserId.getUserId(r.callingUid);
- info.activityInfo = mService.getActivityInfoForUser(info.activityInfo,targetUserId);
+ r.curComponent = component;
+ if (r.callingUid != Process.SYSTEM_UID && isSingleton) {
+ info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
}
r.curReceiver = info.activityInfo;
if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index fb21b06..c80d63a 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -38,6 +38,7 @@
final int uid;
final ApplicationInfo appInfo;
final ComponentName name;
+ final boolean singleton;
public IContentProvider provider;
public boolean noReleaseNeeded;
// All attached clients
@@ -54,12 +55,13 @@
String shortStringName;
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
- ApplicationInfo ai, ComponentName _name) {
+ ApplicationInfo ai, ComponentName _name, boolean _singleton) {
service = _service;
info = _info;
uid = ai.uid;
appInfo = ai;
name = _name;
+ singleton = _singleton;
noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
}
@@ -69,6 +71,7 @@
uid = cpr.uid;
appInfo = cpr.appInfo;
name = cpr.name;
+ singleton = cpr.singleton;
noReleaseNeeded = cpr.noReleaseNeeded;
}
@@ -150,6 +153,9 @@
pw.print(prefix); pw.print("uid="); pw.print(uid);
pw.print(" provider="); pw.println(provider);
}
+ if (singleton) {
+ pw.print(prefix); pw.print("singleton="); pw.println(singleton);
+ }
pw.print(prefix); pw.print("authority="); pw.println(info.authority);
if (full) {
if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index d148ec3..f4d0f9b 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -44,9 +44,9 @@
private static final boolean DBG = false;
- private final HashMap<String, ContentProviderRecord> mGlobalByName
+ private final HashMap<String, ContentProviderRecord> mSingletonByName
= new HashMap<String, ContentProviderRecord>();
- private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
+ private final HashMap<ComponentName, ContentProviderRecord> mSingletonByClass
= new HashMap<ComponentName, ContentProviderRecord>();
private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
@@ -63,7 +63,7 @@
Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
}
// Try to find it in the global list
- ContentProviderRecord record = mGlobalByName.get(name);
+ ContentProviderRecord record = mSingletonByName.get(name);
if (record != null) {
return record;
}
@@ -81,7 +81,7 @@
Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
}
// Try to find it in the global list
- ContentProviderRecord record = mGlobalByClass.get(name);
+ ContentProviderRecord record = mSingletonByClass.get(name);
if (record != null) {
return record;
}
@@ -95,8 +95,8 @@
Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
+ ", record uid = " + record.appInfo.uid);
}
- if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
- mGlobalByName.put(name, record);
+ if (record.singleton) {
+ mSingletonByName.put(name, record);
} else {
final int userId = UserId.getUserId(record.appInfo.uid);
getProvidersByName(userId).put(name, record);
@@ -108,8 +108,8 @@
Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
+ ", record uid = " + record.appInfo.uid);
}
- if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
- mGlobalByClass.put(name, record);
+ if (record.singleton) {
+ mSingletonByClass.put(name, record);
} else {
final int userId = UserId.getUserId(record.appInfo.uid);
getProvidersByClass(userId).put(name, record);
@@ -117,10 +117,10 @@
}
void removeProviderByName(String name, int optionalUserId) {
- if (mGlobalByName.containsKey(name)) {
+ if (mSingletonByName.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByName name=" + name);
- mGlobalByName.remove(name);
+ mSingletonByName.remove(name);
} else {
// TODO: Verify this works, i.e., the caller happens to be from the correct user
if (DBG)
@@ -132,10 +132,10 @@
}
void removeProviderByClass(ComponentName name, int optionalUserId) {
- if (mGlobalByClass.containsKey(name)) {
+ if (mSingletonByClass.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByClass name=" + name);
- mGlobalByClass.remove(name);
+ mSingletonByClass.remove(name);
} else {
if (DBG)
Slog.i(TAG,
@@ -197,37 +197,30 @@
}
void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
- boolean needSep = false;
- if (mGlobalByClass.size() > 0) {
- if (needSep)
- pw.println(" ");
- pw.println(" Published content providers (by class):");
- dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
+ if (mSingletonByClass.size() > 0) {
+ pw.println("");
+ pw.println(" Published single-user content providers (by class):");
+ dumpProvidersByClassLocked(pw, dumpAll, mSingletonByClass);
}
- if (mProvidersByClassPerUser.size() > 1) {
- pw.println("");
- for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
- HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
- pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
- dumpProvidersByClassLocked(pw, dumpAll, map);
- pw.println(" ");
- }
- } else if (mProvidersByClassPerUser.size() == 1) {
- HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0);
+ pw.println("");
+ for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
+ HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
+ pw.println(" Published user " + mProvidersByClassPerUser.keyAt(i)
+ + " content providers (by class):");
dumpProvidersByClassLocked(pw, dumpAll, map);
+ pw.println(" ");
}
- needSep = true;
if (dumpAll) {
- pw.println(" ");
- pw.println(" Authority to provider mappings:");
- dumpProvidersByNameLocked(pw, mGlobalByName);
+ pw.println("");
+ pw.println(" Single-user authority to provider mappings:");
+ dumpProvidersByNameLocked(pw, mSingletonByName);
for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
- if (i > 0) {
- pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + ":");
- }
+ pw.println("");
+ pw.println(" User " + mProvidersByNamePerUser.keyAt(i)
+ + " authority to provider mappings:");
dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
}
}
@@ -328,6 +321,4 @@
}
}
}
-
-
}
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index d788eba..1232846 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -25,7 +25,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
@@ -39,7 +38,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
-import android.os.SystemProperties;
+import android.os.SystemService;
import android.util.Log;
import com.android.internal.R;
@@ -485,8 +484,7 @@
// Wait for the daemons to stop.
for (String daemon : mDaemons) {
- String key = "init.svc." + daemon;
- while (!"stopped".equals(SystemProperties.get(key, "stopped"))) {
+ while (!SystemService.isStopped(daemon)) {
checkpoint(true);
}
}
@@ -519,11 +517,10 @@
// Start the daemon.
String daemon = mDaemons[i];
- SystemProperties.set("ctl.start", daemon);
+ SystemService.start(daemon);
// Wait for the daemon to start.
- String key = "init.svc." + daemon;
- while (!"running".equals(SystemProperties.get(key))) {
+ while (!SystemService.isRunning(daemon)) {
checkpoint(true);
}
@@ -579,8 +576,7 @@
// Check if a running daemon is dead.
for (int i = 0; i < mDaemons.length; ++i) {
String daemon = mDaemons[i];
- if (mArguments[i] != null && !"running".equals(
- SystemProperties.get("init.svc." + daemon))) {
+ if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
throw new IllegalStateException(daemon + " is dead");
}
}
@@ -647,7 +643,7 @@
// Kill the daemons if they fail to stop.
if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
for (String daemon : mDaemons) {
- SystemProperties.set("ctl.stop", daemon);
+ SystemService.stop(daemon);
}
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index cbc3c51..180081b 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -5335,21 +5335,13 @@
final Uri packageURI, final IPackageInstallObserver observer, final int flags,
final String installerPackageName) {
installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,
- null, null, null, null);
- }
-
- public void installPackageWithOrigin(
- Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName, Uri originatingURI, Uri referrer) {
- installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,
- null, null, originatingURI, referrer);
+ null, null);
}
@Override
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
- ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams,
- Uri originatingURI, Uri referrer) {
+ ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int uid = Binder.getCallingUid();
@@ -5367,7 +5359,7 @@
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
- verificationURI, manifestDigest, encryptionParams, originatingURI, referrer);
+ verificationURI, manifestDigest, encryptionParams);
mHandler.sendMessage(msg);
}
@@ -5803,13 +5795,11 @@
private int mRet;
private File mTempPackage;
final ContainerEncryptionParams encryptionParams;
- final Uri originatingURI;
- final Uri referrer;
InstallParams(Uri packageURI,
IPackageInstallObserver observer, int flags,
String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest,
- ContainerEncryptionParams encryptionParams, Uri originatingURI, Uri referrer) {
+ ContainerEncryptionParams encryptionParams) {
this.mPackageURI = packageURI;
this.flags = flags;
this.observer = observer;
@@ -5817,8 +5807,6 @@
this.verificationURI = verificationURI;
this.manifestDigest = manifestDigest;
this.encryptionParams = encryptionParams;
- this.originatingURI = originatingURI;
- this.referrer = referrer;
}
private int installLocationPolicy(PackageInfoLite pkgLite, int flags) {
@@ -6014,14 +6002,6 @@
verificationURI);
}
- if (originatingURI != null) {
- verification.putExtra(Intent.EXTRA_ORIGINATING_URI, originatingURI);
- }
-
- if (referrer != null) {
- verification.putExtra(Intent.EXTRA_REFERRER, referrer);
- }
-
final PackageVerificationState verificationState = new PackageVerificationState(
requiredUid, args);
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 295f324..81a2c14 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -9,7 +9,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
easymocklib \
- guava
+ guava \
+ littlemock
LOCAL_JAVA_LIBRARIES := android.test.runner services
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
new file mode 100644
index 0000000..93ea6a2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.getNetworkTypeName;
+import static android.net.NetworkStateTracker.EVENT_STATE_CHANGED;
+import static com.google.testing.littlemock.LittleMock.anyInt;
+import static com.google.testing.littlemock.LittleMock.createCaptor;
+import static com.google.testing.littlemock.LittleMock.doNothing;
+import static com.google.testing.littlemock.LittleMock.doReturn;
+import static com.google.testing.littlemock.LittleMock.doThrow;
+import static com.google.testing.littlemock.LittleMock.eq;
+import static com.google.testing.littlemock.LittleMock.isA;
+import static com.google.testing.littlemock.LittleMock.mock;
+import static com.google.testing.littlemock.LittleMock.reset;
+import static com.google.testing.littlemock.LittleMock.verify;
+
+import android.content.Context;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.net.LinkProperties;
+import android.net.NetworkConfig;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkStateTracker;
+import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.INetworkManagementService;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.util.LogPrinter;
+
+import com.google.testing.littlemock.ArgumentCaptor;
+
+import java.net.InetAddress;
+import java.util.concurrent.Future;
+
+/**
+ * Tests for {@link ConnectivityService}.
+ */
+@LargeTest
+public class ConnectivityServiceTest extends AndroidTestCase {
+ private static final String TAG = "ConnectivityServiceTest";
+
+ private static final String MOBILE_IFACE = "rmnet3";
+ private static final String WIFI_IFACE = "wlan6";
+
+ private static final RouteInfo MOBILE_ROUTE_V4 = RouteInfo.makeHostRoute(parse("10.0.0.33"));
+ private static final RouteInfo MOBILE_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::33"));
+
+ private static final RouteInfo WIFI_ROUTE_V4 = RouteInfo.makeHostRoute(
+ parse("192.168.0.66"), parse("192.168.0.1"));
+ private static final RouteInfo WIFI_ROUTE_V6 = RouteInfo.makeHostRoute(
+ parse("fd00::66"), parse("fd00::"));
+
+ private INetworkManagementService mNetManager;
+ private INetworkStatsService mStatsService;
+ private INetworkPolicyManager mPolicyService;
+ private ConnectivityService.NetworkFactory mNetFactory;
+
+ private BroadcastInterceptingContext mServiceContext;
+ private ConnectivityService mService;
+
+ private MockNetwork mMobile;
+ private MockNetwork mWifi;
+
+ private Handler mTrackerHandler;
+
+ private static class MockNetwork {
+ public NetworkStateTracker tracker;
+ public NetworkInfo info;
+ public LinkProperties link;
+
+ public MockNetwork(int type) {
+ tracker = mock(NetworkStateTracker.class);
+ info = new NetworkInfo(type, -1, getNetworkTypeName(type), null);
+ link = new LinkProperties();
+ }
+
+ public void doReturnDefaults() {
+ // TODO: eventually CS should make defensive copies
+ doReturn(new NetworkInfo(info)).when(tracker).getNetworkInfo();
+ doReturn(new LinkProperties(link)).when(tracker).getLinkProperties();
+
+ // fallback to default TCP buffers
+ doReturn("").when(tracker).getTcpBufferSizesPropName();
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mServiceContext = new BroadcastInterceptingContext(getContext());
+
+ mNetManager = mock(INetworkManagementService.class);
+ mStatsService = mock(INetworkStatsService.class);
+ mPolicyService = mock(INetworkPolicyManager.class);
+ mNetFactory = mock(ConnectivityService.NetworkFactory.class);
+
+ mMobile = new MockNetwork(TYPE_MOBILE);
+ mWifi = new MockNetwork(TYPE_WIFI);
+
+ // omit most network trackers
+ doThrow(new IllegalArgumentException("Not supported in test environment"))
+ .when(mNetFactory).createTracker(anyInt(), isA(NetworkConfig.class));
+
+ doReturn(mMobile.tracker)
+ .when(mNetFactory).createTracker(eq(TYPE_MOBILE), isA(NetworkConfig.class));
+ doReturn(mWifi.tracker)
+ .when(mNetFactory).createTracker(eq(TYPE_WIFI), isA(NetworkConfig.class));
+
+ final ArgumentCaptor<Handler> trackerHandler = createCaptor();
+ doNothing().when(mMobile.tracker)
+ .startMonitoring(isA(Context.class), trackerHandler.capture());
+
+ mService = new ConnectivityService(
+ mServiceContext, mNetManager, mStatsService, mPolicyService, mNetFactory);
+ mService.systemReady();
+
+ mTrackerHandler = trackerHandler.getValue();
+ mTrackerHandler.getLooper().setMessageLogging(new LogPrinter(Log.INFO, TAG));
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testMobileConnectedAddedRoutes() throws Exception {
+ Future<?> nextConnBroadcast;
+
+ // bring up mobile network
+ mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null);
+ mMobile.link.setInterfaceName(MOBILE_IFACE);
+ mMobile.link.addRoute(MOBILE_ROUTE_V4);
+ mMobile.link.addRoute(MOBILE_ROUTE_V6);
+ mMobile.doReturnDefaults();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ // verify that both routes were added and DNS was flushed
+ verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4));
+ verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6));
+ verify(mNetManager).flushInterfaceDnsCache(MOBILE_IFACE);
+
+ }
+
+ public void testMobileWifiHandoff() throws Exception {
+ Future<?> nextConnBroadcast;
+
+ // bring up mobile network
+ mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null);
+ mMobile.link.setInterfaceName(MOBILE_IFACE);
+ mMobile.link.addRoute(MOBILE_ROUTE_V4);
+ mMobile.link.addRoute(MOBILE_ROUTE_V6);
+ mMobile.doReturnDefaults();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ reset(mNetManager);
+
+ // now bring up wifi network
+ mWifi.info.setDetailedState(DetailedState.CONNECTED, null, null);
+ mWifi.link.setInterfaceName(WIFI_IFACE);
+ mWifi.link.addRoute(WIFI_ROUTE_V4);
+ mWifi.link.addRoute(WIFI_ROUTE_V6);
+ mWifi.doReturnDefaults();
+
+ // expect that mobile will be torn down
+ doReturn(true).when(mMobile.tracker).teardown();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mWifi.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ // verify that wifi routes added, and teardown requested
+ verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V4));
+ verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V6));
+ verify(mNetManager).flushInterfaceDnsCache(WIFI_IFACE);
+ verify(mMobile.tracker).teardown();
+
+ reset(mNetManager, mMobile.tracker);
+
+ // tear down mobile network, as requested
+ mMobile.info.setDetailedState(DetailedState.DISCONNECTED, null, null);
+ mMobile.link.clear();
+ mMobile.doReturnDefaults();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4));
+ verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6));
+
+ }
+
+ private static InetAddress parse(String addr) {
+ return InetAddress.parseNumericAddress(addr);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 79872f3..10ac153 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -90,6 +90,7 @@
public static final int CMD_SET_USER_DATA_ENABLE = BASE + 30;
public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
+ public static final int EVENT_ICC_CHANGED = BASE + 33;
/***** Constants *****/
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 92c6676..9acffa3 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -285,11 +285,6 @@
}
@Override
- public void sendBroadcastToUser(Intent intent, int userId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public void sendBroadcast(Intent intent, String receiverPermission) {
throw new UnsupportedOperationException();
}
@@ -308,6 +303,18 @@
}
@Override
+ public void sendBroadcastToUser(Intent intent, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendOrderedBroadcastToUser(Intent intent, int userId,
+ BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void sendStickyBroadcast(Intent intent) {
throw new UnsupportedOperationException();
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index b1224d3..0399b3b 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -563,17 +563,7 @@
@Override
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
- ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams,
- Uri originatingURI, Uri referrer) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @hide
- */
- @Override
- public void installPackageWithOrigin(Uri packageURI, IPackageInstallObserver observer,
- int flags, String installerPackageName, Uri originatingURI, Uri referrer) {
+ ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
throw new UnsupportedOperationException();
}
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 6f00095..9dfe4a1 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -33,5 +33,11 @@
</service>
<receiver android:name="UserTarget">
</receiver>
+ <receiver android:name="SingleUserReceiver"
+ android:singleUser="true" android:exported="true" >
+ </receiver>
+ <provider android:name="SingleUserProvider"
+ android:authorities="com.google.android.test.activity.single_user"
+ android:singleUser="true" android:exported="true" />
</application>
</manifest>
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index bcef2d9..0ec1f13 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -22,16 +22,20 @@
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProviderClient;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.graphics.Bitmap;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ScrollView;
+import android.widget.Toast;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -43,6 +47,18 @@
ActivityManager mAm;
+ class BroadcastResultReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Bundle res = getResultExtras(true);
+ int user = res.getInt("user", -1);
+ Toast.makeText(ActivityTestMain.this,
+ "Receiver executed as user "
+ + (user >= 0 ? Integer.toString(user) : "unknown"),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
private void addThumbnail(LinearLayout container, Bitmap bm,
final ActivityManager.RecentTaskInfo task,
final ActivityManager.TaskThumbnails thumbs, final int subIndex) {
@@ -134,8 +150,35 @@
});
menu.add("Send!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(ActivityTestMain.this, SingleUserReceiver.class);
+ sendOrderedBroadcast(intent, null, new BroadcastResultReceiver(),
+ null, Activity.RESULT_OK, null, null);
+ return true;
+ }
+ });
+ menu.add("Call!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override public boolean onMenuItemClick(MenuItem item) {
+ ContentProviderClient cpl = getContentResolver().acquireContentProviderClient(
+ SingleUserProvider.AUTHORITY);
+ Bundle res = null;
+ try {
+ res = cpl.call("getuser", null, null);
+ } catch (RemoteException e) {
+ }
+ int user = res != null ? res.getInt("user", -1) : -1;
+ Toast.makeText(ActivityTestMain.this,
+ "Provider executed as user "
+ + (user >= 0 ? Integer.toString(user) : "unknown"),
+ Toast.LENGTH_LONG).show();
+ cpl.release();
+ return true;
+ }
+ });
+ menu.add("Send to user 1!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override public boolean onMenuItemClick(MenuItem item) {
Intent intent = new Intent(ActivityTestMain.this, UserTarget.class);
- sendBroadcastToUser(intent, 1);
+ sendOrderedBroadcastToUser(intent, 1, new BroadcastResultReceiver(),
+ null, Activity.RESULT_OK, null, null);
return true;
}
});
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserProvider.java b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserProvider.java
new file mode 100644
index 0000000..83785e4
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 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.google.android.test.activity;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+
+public class SingleUserProvider extends ContentProvider {
+ static final String AUTHORITY = "com.google.android.test.activity.single_user";
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ Bundle res = new Bundle();
+ res.putInt("user", Process.myUserHandle());
+ return res;
+ }
+}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserReceiver.java
new file mode 100644
index 0000000..9295cf48
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 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.google.android.test.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Process;
+
+public class SingleUserReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Bundle res = getResultExtras(true);
+ res.putInt("user", Process.myUserHandle());
+ setResultExtras(res);
+ }
+}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java b/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java
index 9890483..9c6a9f1 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java
@@ -19,11 +19,16 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
+import android.os.Process;
import android.util.Log;
public class UserTarget extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("ActivityTest", "Received: " + intent);
+ Bundle res = getResultExtras(true);
+ res.putInt("user", Process.myUserHandle());
+ setResultExtras(res);
}
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index fad5993..1857033 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -33,6 +33,15 @@
<meta-data android:name="android.graphics.renderThread" android:value="true" />
<activity
+ android:name="TextPathActivity"
+ android:label="_TextPath">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="GradientStopsActivity"
android:label="_GradientStops">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextPathActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextPathActivity.java
new file mode 100644
index 0000000..35a1fc9
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextPathActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ScrollView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TextPathActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ScrollView scroller = new ScrollView(this);
+ scroller.addView(new CustomTextView(this));
+ setContentView(scroller);
+ }
+
+ static class CustomTextView extends View {
+ private final Paint mHugePaint;
+
+ CustomTextView(Context c) {
+ super(c);
+
+ mHugePaint = new Paint();
+ mHugePaint.setAntiAlias(true);
+ mHugePaint.setColor(0xff000000);
+ mHugePaint.setTextSize(300f);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), 3000);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ Path path = new Path();
+
+ canvas.translate(100.0f, 300.0f);
+ drawTextAsPath(canvas, "Hello", path);
+
+ canvas.translate(0.0f, 400.0f);
+ drawTextAsPath(canvas, "OpenGL", path);
+ }
+
+ private void drawTextAsPath(Canvas canvas, String text, Path path) {
+ int count = text.length();
+ mHugePaint.getTextPath(text, 0, count, 0, 0, path);
+ path.close();
+ canvas.drawPath(path, mHugePaint);
+ }
+ }
+}
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 0dba950..dcbe7db 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -511,7 +511,8 @@
namespaces.pop();
} else if (code == ResXMLTree::TEXT) {
size_t len;
- printf("%sC: \"%s\"\n", prefix.string(), String8(block->getText(&len)).string());
+ printf("%sC: \"%s\"\n", prefix.string(),
+ ResTable::normalizeForOutput(String8(block->getText(&len)).string()).string());
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3ae660d..c4a6906 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1187,6 +1187,18 @@
}
@Override
+ public void sendBroadcastToUser(Intent intent, int userId) {
+ // pass
+ }
+
+ @Override
+ public void sendOrderedBroadcastToUser(Intent intent, int userId,
+ BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ // pass
+ }
+
+ @Override
public void sendStickyBroadcast(Intent arg0) {
// pass